Whenever the implementation of a class involves dynamically allocated memory, the class should explicitly define:
The C++ keyword delete is used to release dynamically allocated memory so that it may be reused.
Dynamic variables (memory allocated by newshould be deleted when they will no longer be used. Deleted dynamic variables should NOT be reused unless that have been reallocated with another call tonew.int *i = new int *i = 10; // some uses of i (and *i) ... // i no longer needed delete i; // OK int x = 2; int *y = &x; // y gets the address of x delete y; // BAD - y NOT dynamically allocated int *i = new int // some uses of i (and *i) delete i; *i = 10; // VERY BAD - i has been deleted int *i = new int // some uses of i (and *i) delete i; i = new int; *i = 10; // OK *i has been reallocated
From StringList::remove() we have delete location. location is a pointer to a ListItem so, destructor for ListItem is invoked. In turn, since ListItem has member String val, the destructor for String is invoked.
Default destructors call destructors of member objects, but do NOT delete pointers to objects. Thus, we need to write destructors that explicitly call delete.
The destructor for a class is a member function that is automatically activated in three cases:
{ StringList L; // some uses of L } // L.~StringList() automatically called
Foo *f = new Foo; // some uses of f (and *f) delete f; // f.~Foo() automatically called
An object's destructor always invokes the destructors of its member objects (not for member variables that are pointers to objects) regardless of whether or not such calls are made explicitly in the destructor. For example, suppose we have:
class Foo { public: ~Foo(); // ... }; class Bar { public: Foo f; Foo *p; ~Bar(){delete p;} }; int main() { Bar b; b.p = new Foo; return 1; }Upon exit from the main function, the destructor for b (b.~Bar()) is automatically invoked. The explicit definition of the destructor calls delete p which then invokes Foo's destructor for the member object *p. The destructor then automatically calls the destructor for the member object f, even though no explicit call is made within the ~Bar code we have written.
Assignments are from values to variables. If x and y are variables then x = y means the variable x is assigned the value of variable y. Immediately following the assignment the values of variables x and y are identical.
In the case of non-pointers, such as
int x = 3; int y = 5; x = y;things are `well behaved'. Now consider:
int *p = new int(1); int *q = new int(2); p = q;The value of q (NOT *q) is copied to p. That means that the pointer is copied. This means that at a later assignment of the form *p = 3 the value 3 is copied to only one place (*p), but (*q) is the same, so both *p an d *q change. So, with pointer assigned associated dynamic variables become the same.
class Foo{ public: int i; int *p; } Foo f,g; f.i = 6; f.p = new int(4); g.i = 1; g.p = new int(2); g = fAssignment occurs at each member variable so it's as if there were these two assignments:
g.i = f.i; // non-pointer assignment g.p = f.p; // pointer assignmentThis is a shallow copy. It is called shallow because on the surface it appear that there are now two separate copies of 4 (*(f.p) and *(g.p)), but in fact there is only one copy since *(f.p) and *(g.p) are the same dynamic variable. There are two big problems with this:
void Foo::operator=(const Foo& source) { // do nothing if the assignment is of form x = x if (&source != this) { i = source.i; delete p; // free old *p memory p = new int // allocate new *p memory *p = *(source.p); } }In general, our prescription for assignment for objects that use dynamic memory is:
Sometimes we want/need to create a new object that is identical to an existing object. This operation is called the copy constructor. It's behavior is nearly identical to the assignment except that since this is the first allocation of dynamic memory for the new object then no memory need be released at this time.
Copy constructors need to be written explicitly in exactly the situation when assignment needs to be written explicitly: when an object uses dynamically allocated memory. For example:
Foo::Foo(const Foo& source) // copy constructor for class Foo { i = source.i; p = new int; *p = *(source.p); } Foo x(y); // create a new Foo x, initialized as a copy of yCopying of objects occurs in three cases:
String s = t; StringList L(M);