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
X
is created, one ofX
's constructors is invoked. -
Whenever an object of type
X
is 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
