“Classes and Pointers: Some Peculiarities.
If a pointer variable is of a class type, we discussed, in the previous section, how to access class members via the pointer by using the arrow notation. Because a class can have pointer data members, this section describes some peculiarities of such classes. To facilitate the discussion, we use the following class:
Code:
class pointerDataClass
{
public:
…
private:
int x;
int lenP;
int *p;
};
Also consider the following statements:
pointerDataClass objectOne;
pointerDataClass objectTwo;
Destructor.
The object objectOne has a pointer data member p. Suppose that during program execution the pointer p creates a dynamic array. When objectOne goes out of scope, all data members of objectOne are destroyed. However p created a dynamic array, and dynamic memory must be deallocated using the operator delete. Therefore, if the pointer p does not use the delete operator to deallocate the dynamic array, the memory space of the dynamic array would stay marked as allocated, even though no one can access it. This is known as a ‘memory leak.’ How do we ensure that when p is destroyed, the dynamic memory created by p is also destroyed? Suppose that objectOne is as shown in fig3-16. (p points to an array outside the object loaded with integers {5, 36, 24, 15, …}
Recall that if a class has a destructor, the destructor automatically executes whenever a class object goes out of scope. Therefore, we can put the necessary code in the destructor to ensure that when objectOne goes out of scope, the memory created by the pointer p is deallocated. This is one of the main purposes of including a destructor. For example, the definition of the destructor for the class pointerDataClass is:
Code:
PointerDataClass::~pointerDataClass()
{
delete [] p;
}
Of course, you must include the destructor as a member of the class in its definition. Let us extend the definition of the class pointerDataClass by including the destructor. Moreover, the remainder of this section assumes that the definition of the destructor is as given previously – that is, the destructor deallocates the memory space pointed to by p.
Code:
class pointerDataClass
{
public:
~pointerDataClass();
…
private:
int x;
int lenP;
int *p;
};
NOTE: for the destructor to work properly, the pointer p must have a valid value. If p is not properly initialized (that is, if the value of p is garbage) and the destructor executes, either the program terminates with an error message or the destructor deallocates an unrelated memory space. For this reason, you should exercise caution while working with pointer.
Assignment Operator:
This section describes the limitations of the built-in assignment operators for classes with pointer data members. Suppose that objectOne and objectTwo are as shown in fig 3-17. (objectOne.x = 8, objectOne.lenP = 50, and objectOne.p points to an array of integers {5, 36, 24, 15, …} while objectTwo.x, objectTwo.lenP, and objectTwo.p are empty) Recall that one of the built-in operations on classes is the assignment operator. For example the statement:
objectTwo = objectOne;
copies the data members of objectOne into objectTwo. That is, the value of objectOne.x is copied into objectTwo.x, the value of objectOne.lenP is copied into objectTwo.lenP, and the value of objectOne.p is copied into objectTwo.p. Because p is a pointer, this member-wise copying of data would lead to a shallow copying of the data. That is, both objectTwo.p and objectOne.p would point to the same memory space, as shown in fig 3-18. (you all know what the picture looks like objectOne.p and objectTwo.p point to the same array) Now, if objectTwo.p deallocates the memory space to which it points, objectOne.p would become invalid. This situation could very well happen, if class pointerDataClass has a destructor that deallocates the memory space pointes to by p when an object of the type pointerDataClass goes out of scope. It suggests that there must be a way to avoid this pitfall. To avoid this shallow copying of data for classes with a pointer data member, C++ allows the programmer to extend the definition of the assignment operator. This process is called overloading the assignment operator. Once the assignment operator is properly overloaded, both the objects objectOne and objectTwo have their own data, as shown in fig 3-19. (again, you all know what the picture looks like – objectOne.p points to an array and objectTwo.p points to an array different than objectOne.p bet they are identical)”