
//
// This is example code from Chapter 18.5.4 "Pointer problems" of
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//

//------------------------------------------------------------------------------
#include<iostream>

class vector {
    int sz;
    double* elem;
    void copy(const vector& arg);                 // copy elements from arg into *elem
public:
    vector(int s) :sz(s), elem(new double[s]) { } // constructor
    vector(const vector&) ;                       // copy constructor: define copy
    ~vector() { delete[] elem; }                  // destructor

    int size() const { return sz; }               // the current size

    double get(int n) { return elem[n]; }         // access: read
    void set(int n, double v) { elem[n]=v; }      // access: write
    double& operator[](int n) { return elem[n]; } // return reference
    double operator[](int n) const  { return elem[n]; }// for const vectors
    ///double* operator[](int n) { return &elem[n]; } // return element
    // ...
};

//------------------------------------------------------------------------------

void fct_that_can_receive_a_0(int* p)
{
    if (p == 0) {
        // do something
    }
    else {
        // use p
        *p =7;
    }
}

//------------------------------------------------------------------------------

int* fct_that_can_return_a_0()
{
    return 0;
}

//------------------------------------------------------------------------------

// Don't return a pointer to a local variable:
int* f()
{
    int x = 7;
    // ...
    return &x;
}

//------------------------------------------------------------------------------

/**vector& ff()
{
    vector x(7);
    // ...
    return x;
}    // the vector x is destroyed here
*/


void ff(const vector& cv, vector& v) {
    double d = cv[1]; /// fine (uses the const [])
    std::cout << "\nd = " << d;
    ///cv[1] = 2.0; /// error (uses the const [])
    double d2 = v[1]; /// fine (uses the non-const [])
    std::cout << "\nd2 = " << d2;
    v[1] = 2.0; /// fine (uses the non-const [])
    std::cout << "\nv[1] = " << v[1];

}

int main()
{
    vector v(10);
    double x = v[2]+1.1; /// fine
    v[3] = x; /// error: v[3] is not an lvalue
    std::cout << "\nv[3] = " << v[3];
    v[3] = 3.14;
    double y = v[3];
    std::cout << "\n&y = " << &y;
    //cout << v[3]

    const vector cv(10);
    ff(cv,v);
    /// Don't access through the null pointer:
    /**{
        int* p = nullptr;
        *p = 7;      /// Ouch!
    }

    int* p = fct_that_can_return_a_0();
    if (p == 0) {
        // do something
    }
    else {
        // use p
        *p = 7;
    }

    // Do initialize your pointers:
    {
        int* p;
        *p = 9;      // Ouch!
    }

    // Don't access non-existing array elements:
    {
        int a[10];
        int* p = &a[10];
        *p = 11;     // Ouch!
        a[10] = 12;  // Ouch!
    }

    // Don't access through a deleted pointer:
    {
        int* p = new int(7);
        // ...
        delete p;
        // ...
        *p = 13;     // Ouch!
    }

    // Don't return a pointer to a local variable:
    {
        int* p = f();
        // ...
        *p = 15;     // Ouch!

    }

    /// Consider a logically equivalent example:
    {
        vector& p = ff();
        // ...
        p[4] = 15;     // Ouch!
    }*/
}

//------------------------------------------------------------------------------
