18. Vectors and Arrays
"Caveat emptor!" -- Ancient advice
18.1 Introduction
 
                        We are looking at how to go from the hardware supported types "upward" to types that act the way users, not hardware, want.
In this chapter we focus on copying, and how it relates to initialization, cleanup, equality testing, etc.
18.2 Initialization
 
                        
                        We usually should initialize our variables rather than
                        accepting the default value. Initializer lists are in
                        curly braces: {}.
                        We can write:
                    
                    
                        
                    
vector v = {1, 2, 3];
                        
                    
Or:
                    
                        
                    
vector v = {1, 2, 3];
                        
                    
18.3 Copying
 
                        
                        Consider an implementation of vector
                        like the following:
                    
                    
                        
                    
class vect {
    int sz;
    double* elem;
public:
    vect(int s)
        :sz{s}, elem{new double [s]}  { /*  */ }
    ~vect()
        { delete[] elem; }
    double get(int i) { return elem[i]; }
    void set(int i, double d) { elem[i] = d; }
};
                        
                    
Then what happens in the following code?
                    
                        
                    
void f(int n)
{
    vect v(3);
    v.set(2, 2.2);
    vect v2 = v;
    v.set(1, 9.9);
    v2.set(0, 8.8);
    cout << v.get(0) << ' ' << v2.get(1);
}
                        
                    
Disaster!
18.3.1 Copy constructors
What to do? Create a copy constructor.
                        
                            
                        
vector::vector(const vector& arg)
// allocate space, then initialize via copy
    :sz{arg.sz}, elem{new double[arg.sz]}
{
    copy(arg.elem, arg.elem+arg.sz, elem);  // from std lib
}
                            
                        
18.3.2 Copy assignments
We get the same problem as above, plus a memory leak, on default assignments. Here's what we need to do:
                        
                            
                        
vector& vector::operator=(const vector& a)
// make this vector a copy of a
{
    double* p = new double[a.sz];
    copy(a.elem, a.elem+a.sz, p);
    delete[] elem;
    elem = p;
    sz = a.sz;
    return *this;  // return self-ref
}
                            
                        
18.3.3 Copy terminology
Let's look at shallow copies versus deep copies.
In a shallow copy both variables point to the same memory.
In a deep copy each variable points to different memory.
18.3.4 Moving
 
                            We can write a move assignment operator when we want to explicitly re-use the storage for one object in another. It looks like this:
                        
                            
                        
vector& vector::operator=(vector&& a)
                            
                        
The compiler will call this automatically when it sees an assigned element is going out of scope.
Drill
                        vec1.cpp:
                        
                        Write a vector class with the default
                        copy constructor and assignment operator.
                        write a main with a function like
                        f() above. Loop calling it and see
                        what happens.
                    
                        vec2.cpp:
                        
                        Copy that code and add proper copy constructor and 
                        assignment operator. Run your loop again.
                        Things should work now.
                    
18.4 Essential operations
 
                        The C++ FDA recomments the following "7 essential operations" for a class:
- Constructor with args
- Default constructor
- Copy constructor
- Copy assignment
- Move constructor
- Move assignment
- Destructor
18.4.1 Explicit constructors
                            The keyword explicit prevents
                            default type conversions for objects when
                            we don't want them. Good example:
                        
                        
                            
                        
class complex {
    public:
        complex(double); // construct a complex # from a double
};
complex z = 3.14;  // OK!
                            
                        
Bad example:
                        
                            
                        
vector v = 10;
v = 20;  // this would assign a new vector of 20 doubles to v!
                            
                        
18.4.2 Debugging constructors and destructors
 
                            These are not called just where you see them! Rule:
- 
                                Whenever an object of type Xis created, one ofX's constructors is invoked.
- 
                                Whenever an object of type Xis destroyed,X's destructor is invoked.
Drill
Do the exercise on pages 644-645 of the textbook.
18.5 Access to vector elements
                        We have used get() and
                        set() methods to access
                        elements of our vector so far. We can
                        instead use the array access operator if we
                        overload it as follows:
                    
                    
                        
                    
class vect {
    // .. our other code!
    double& operator[](int n) { return elem[n]; }
};
                        
                    
18.5.1 Overloading on const
                            The above won't allow something
                            like x = v[7] if
                            v is a const in that context
                            because it can't tell we aren't going to change
                            v. We can write a second
                            version that provides access to elements
                            in a read-only fashion:
                        
                    
                        
                    
class vect {
    // .. our other code!
    double operator[](int n) const { return elem[n]; }
};
                        
                    
Drill
                        Overload the [] operator for your
                        vect class.
                    
18.6 Arrays
                        Short version of section: you should use 
                        vector, not arrays! But we need to 
                        understand arrays to read old code, C code,
                        and so on. Let's say we are having trouble
                        with Python lists, and we need to look at
                        the
                        source code: we'd better understand arrays!
                    
18.6.1 Pointers to array elements
We can point into the middle of an array:
                        
                            
                        
double ad[10];
double* p = &ad[10];
                            
                        
18.6.2 Pointers and arrays
An array can "turn into" a pointer:
                        
                            
                        
char ch[100];
char* p = ch;
                            
                        
                            sizeof(ch) is 100.
                            But sizeof(p) is 4 (on 32-bit systems)
                            or 8 (64-bit).
                        
18.6.3 Array initialization
We can initialize character arrays with a string, like this:
                        
                            
                        
char ac[] = "Beorn";
                            
                        
This will create a six byte array:
18.6.4 Pointer problems
 
                            A quick summary of some pointer problems:
- Acces through the null pointer
- Access through an unitialized pointer
- Access off the end of an array
- Access to a deallocated object
- Access to an object that has gone out of scope
18.7 Examples: palindrome
18.7.1 Palindromes using string
18.7.2 Palindromes using arrays
18.7.3 Palindromes using pointers
Test Yourself!
 
                        - A good use of shallow copying would be when
- we have a very large object to copy
- we just need to scoop out the top few values from an object
- we don't really care if all the field values are the same
- A good use of deep copying would be when
- we need to make sure the values in one object change when values in the other do
- we need to make sure each objects uses separate memory
- we need to save memory
- We want a default constructor when
- we can establish meaningful invariants using default values
- we really don't know what should initialize a class
- we want to eliminate the faults (defaults) from our classes
- We need a destructor when our class
- has many members
- has default values
- has no copy constructor
- acquires resources
- 
                If we write char prof = "Callahan";that will create a string of ___ bytes:
- 12
- depends on the compiler
- 8
- 9
- Among common pointer problems are:
- access off the end of an array
- access through the null pointer
- all answers are correct
- access to a deallocated object
- In C++, we can give our own classes array-style access by:
- overloading the [] operator
- all answers are correct
- 
                
                writing get()andset()functions
- using pointers to pointers
- 
                If we write char ch[100]thensizeof(ch)will be:
- 8
- 4
- 200
- 100
- If we have a pointer in 32-bit Windows, its size most likely is
- the size of whatever it points to
- 8
- 4
Answers
1. a; 2. b; 3. a; 4. d; 5. d; 6. c; 7. a; 8. d; 9. c;
Drill
