E N D
1. CSci 152: Programming IIFall 2004 Shallow vs. Deep Copy
Classes and Pointers
2. SHALLOW VERSUS DEEP COPY AND POINTERS
int *p;
p = new int;
The first statement declares p to be a pointer variable of the type int.
The second statement allocates memory of the type int, and the address of the allocated memory is stored in
3. *p = 87;
4. second = first; //Line ***
5. delete [] second;
6. second = new int[10];
for(int j = 0; j < 10; j++)
second[j] = first[j];
7. CLASSES AND POINTERS: SOME PECULIARITIES
class pointerDataClass
{
public:
...
private:
int x;
int lenP;
int *p;
};
pointerDataClass objectOne;
pointerDataClass objectTwo;
8. The 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.
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.
How do we ensure that when p is destroyed, the dynamic memory created by p is also destroyed?
9. If a class has a destructor, the destructor automatically executes whenever a class object goes out of scope.
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.
pointerDataClass::~pointerDataClass()
{
delete [] p;
}
class pointerDataClass
{
public:
~pointerDataClass();
...
private:
int x;
int lenP;
int *p;
};
10. The Assignment Operator
11. objectTwo = objectOne;
12. 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.
Chapter 16 explains how to accomplish this task by using operator overloading.
Once the assignment operator is properly overloaded, both the objects objectOne and objectTwo have their own data, as shown in Figure 15-19.
13.
14. void destroyList(pointerDataClass paramObject);
destroyList(objectOne);
15. If a class has pointer data members:
During object declaration, the initialization of one object using the value of another object would lead to shallow copying of data if the default member-wise copying of data is allowed.
If, as a parameter, an object is passed by value and the default member-wise copying of data is allowed, it would lead to shallow copying of data.
In both cases, to force each object to have its own copy of the data, we must override the definition of the copy constructor provided by the compiler.
This is usually done by putting a statement that includes the copy constructor in the definition of the class, and then writing the definition of the copy constructor.
For the class pointerDataClass, we can overcome this shallow copying of data problem by including the copy constructor in the class pointerDataClass.
16. The copy constructor automatically executes in two situations (as described in the previous list):
When an object is declared and initialized by using the value of another object
When, as a parameter, an object is passed by value
Once the copy constructor is properly defined for the class pointerDataClass, both objectOne.p and objectThree.p will have their own copies of the data. Similarly, objectOne.p and paramObject.p will have their own copies of the data.
17. Example 15-5
class pointerDataClass
{
public:
void print() const;
void setData();
void destroyP();
pointerDataClass(int sizeP = 10);
~pointerDataClass();
pointerDataClass (const pointerDataClass& otherObject);
//the copy constructor
private:
int x;
int lenP;
int *p; //pointer to an int array
};
18. void pointerDataClass::print() const
{
cout<<"x = "<<x<<endl;
cout<<"p = ";
for(int i = 0; i < lenP; i++)
cout<<p[i]<<" ";
cout<<endl;
}
void pointerDataClass::setData()
{
cout<<"Enter an integer for x: ";
cin>>x;
cout<<endl;
cout<<"Enter "<<lenP<<" numbers: ";
for(int i = 0; i < lenP; i++)
cin>>p[i];
cout<<endl;
}
19. void pointerDataClass::destroyP()
{
lenP = 0;
delete [] p;
p = NULL;
}
pointerDataClass::pointerDataClass(int sizeP)
{
x = 0;
if(sizeP <= 0)
{
cout<<"Array size must be positive"<<endl;
cout<<"Creating an array of size 10"<<endl;
lenP = 10;
}
else
lenP = sizeP;
p = new int[lenP];
}
20. pointerDataClass::~pointerDataClass()
{
delete [] p;
}
//copy constructor
pointerDataClass::pointerDataClass
(const pointerDataClass& otherObject)
{
x = otherObject.x;
lenP = otherObject.lenP;
p = new int[lenP];
for(int i = 0; i < lenP; i++)
p[i] = otherObject.p[i];
}
21. #include <iostream>
#include "ptrDataClass.h"
using namespace std;
void testCopyConst(pointerDataClass temp);
int main()
{
pointerDataClass one(5); //Line 1
one.setData(); //Line 2
cout<<"Line 3: ###Object one's data###"<<endl; //Line 3
one.print(); //Line 4
cout<<"Line 5:______________________________"
<<"______________"<<endl; //Line 5
pointerDataClass two(one); //Line 6
cout<<"Line 7: ^^^Object two's data^^^"<<endl; //Line 7
two.print(); //Line 8
cout<<"Line 9:_______________________________"
<<"_____________"<<endl; //Line 9
two.destroyP(); //Line 10
cout<<"Line 11: ~~~ Object one's data after "
<<"destroying object two.p ~~~"<<endl; //Line 11
22. one.print(); //Line 12
cout<<"Line 13:_______________________________"
<<"_____________"<<endl; //Line 13
cout<<"Line 14: Calling the function testCopyConst"
<<endl; //Line 14
testCopyConst(one); //Line 15
cout<<"Line 16:_______________________________"
<<"_____________"<<endl; //Line 16
cout<<"Line 17: After a call to the function "
<<"testCopyConst, object one is:"<<endl; //Line 17
one.print(); //Line 18
return 0; //Line 19
}
23. void testCopyConst(pointerDataClass temp)
{
cout<<"Line 20: *** Inside function "
<<"testCopyConst ***"<<endl; //Line 20
cout<<"Line 21: Object temp data:"<<endl; //Line 21
temp.print(); //Line 22
temp.setData(); //Line 23
cout<<"Line 24: After changing the object "
<<"temp, its data is: "<<endl; //Line 24
temp.print(); //Line 25
cout<<"Line 26: *** Exiting function "
<<"testCopyConst ***"<<endl; //Line 26
}
24. Sample Run: In this sample run, the user input is in red.
Enter an integer for x: 28
Enter 5 numbers: 2 4 6 8 10
Line 3: ###Object one's data###
x = 28
p = 2 4 6 8 10
Line 5:____________________________________________
Line 7: ^^^Object two's data^^^
x = 28
p = 2 4 6 8 10
Line 9:____________________________________________
Line 11: ~~~ Object one's data after destroying object two.p ~~~
x = 28
p = 2 4 6 8 10
Line 13:____________________________________________
Line 14: Calling the function testCopyConst
Line 20: *** Inside function testCopyConst ***
Line 21: Object temp data:
x = 28
p = 2 4 6 8 10
25.
Enter an integer for x: 65
Enter 5 numbers: 1 3 5 7 9
Line 24: After changing the object temp, its data is:
x = 65
p = 1 3 5 7 9
Line 26: *** Exiting function testCopyConst ***
Line 16:____________________________________________
Line 17: After a call to the function testCopyConst, object one is:
x = 28
p = 2 4 6 8 10
26. INHERITANCE, POINTERS, AND VIRTUAL FUNCTIONS
C++ allows the user to pass an object of a derived class to a formal parameter of the base class type.
class baseClass
{
public:
void print();
baseClass(int u = 0);
private:
int x;
};
class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};
27. void baseClass::print()
{
cout<<"In baseClass x = "<<x<<endl;
}
baseClass::baseClass(int u)
{
x = u;
}
void derivedClass::print()
{
cout<<"In derivedClass ***: ";
baseClass::print();
cout<<"In derivedClass a = "<<a<<endl;
}
derivedClass::derivedClass(int u, int v)
: baseClass(u)
{
a = v;
}
28. void callPrint(baseClass& p)
{
p.print();
}
int main()
{
baseClass one(5); //Line 1
derivedClass two(3, 15); //Line 2
one.print(); //Line 3
two.print(); //Line 4
cout<<"*** Calling the function callPrint ***"
<<endl; //Line 5
callPrint(one); //Line 6
callPrint(two); //Line 7
return 0;
}
29. Output
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
*** Calling the function callPrint ***
In baseClass x = 5
In baseClass x = 3
30. In compile-time binding, the necessary code to call a specific function is generated by the compiler.
Compile-time binding is also known as static binding.
For the statement in Line 7, the actual parameter is of the type derivedClass.
When the body of the function two executes, logically the print function of object two should execute, which is not the case.
C++ corrects this problem by providing the mechanism of virtual functions.
The binding of virtual functions occurs at program execution time, not at compile time.
This kind of binding is called run-time binding.
In run-time binding, the compiler does not generate code to call a specific function; instead, it generates enough information to enable the run-time system to generate the specific code for the appropriate function call.
Run-time binding is also known as dynamic binding.
31. class baseClass
{
public:
virtual void print(); //virtual function
baseClass(int u = 0);
private:
int x;
};
class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};
32. If we execute the previous program with these modifications, the output is as follows.
Output
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
*** Calling the function callPrint ***
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
33. //Chapter 15: Virtual Functions
#include <iostream>
#include "classExtTestVirtual.h"
using namespace std;
void callPrint(baseClass *p);
int main()
{
baseClass *q; //Line 1
derivedClass *r; //Line 2
q = new baseClass(5); //Line 3
r = new derivedClass(3,15); //Line 4
q->print(); //Line 5
r->print(); //Line 6
cout<<"*** Calling the function callPrint ***"
<<endl; //Line 7
callPrint(q); //Line 8
callPrint(r); //Line 9
return 0;
}
34.
void callPrint(baseClass *p)
{
p->print();
}
Output
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
*** Calling the function callPrint ***
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
35. //Chapter 15: Virtual Functions and value parameters
#include <iostream>
#include "classExtTestVirtual.h"
using namespace std;
void callPrint(baseClass p);
int main()
{
baseClass one(5); //Line 1
derivedClass two(3, 15); //Line 2
one.print(); //Line 3
two.print(); //Line 4
cout<<"*** Calling the function callPrint ***"
<<endl; //Line 5
callPrint(one); //Line 6
callPrint(two); //Line 7
return 0;
}
36.
void callPrint(baseClass p) //p is a value parameter
{
p.print();
}
Output
In baseClass x = 5
In derivedClass ***: In baseClass x = 3
In derivedClass a = 15
*** Calling the function callPrint ***
In baseClass x = 5
In baseClass x = 3
If a base class contains virtual functions, make the destructor of the base class virtual.
37. THE ADDRESS OF OPERATOR AND CLASSES
The address of operator is also used to create aliases to an object.
Consider the following statements:
int x;
int &y = x;
Both x and y refer to the same memory location.
y is like a constant pointer variable.
The statement
y = 25;
sets the value of y and hence of x to 25. Similarly the statement
x = 2 * x + 30;
updates the value of x and hence of y.
38. The address of operator, &, can also be used to return the address of private data members of a class. However, if we are not careful this can result in serious errors in the program.
//header file testadd.h
#ifndef H_testAdd
#define H_testAdd
class testAddress
{
public:
void setX(int);
void printX() const;
int& addressOfX(); //this function returns the
//address of the private data member
private:
int x;
};
#endif
39. //Implementation file testAdd.cpp
#include <iostream>
#include "testAdd.h"
using namespace std;
void testAddress::setX(int inX)
{
x = inX;
}
void testAddress::printX() const
{
cout<<x;
}
int& testAddress::addressOfX()
{
return x;
}
40. //Test program
#include <iostream>
#include "testAdd.h"
using namespace std;
int main()
{
testAddress a;
int &y = a.addressOfX();
a.setX(50);
cout<<"x in class testAddress = ";
a.printX();
cout<<endl;
y = 25;
cout<<"After y = 25, x in class testAddress = ";
a.printX();
cout<<endl;
return 0;
}
Output
x in class testAddress = 50
After y = 25, x in class testAddress = 25
41. #ifndef H_testAdd
#define H_testAdd
class testAddress
{
public:
void setX(int);
void printX() const;
const int& addressOfX(); //this function returns the
//address of the private data
//member
private:
int x;
};
#endif
const int& testAddress::addressOfX()
{
return x;
}
42. The definition of the function addressOfX in the implementation file is:
const int& testAddress::addressOfX()
{
return x;
}
The same program now will generate compile time error.