1 / 41

CSci 152: Programming II Fall 2004

xandy
Download Presentation

CSci 152: Programming II Fall 2004

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


    1. CSci 152: Programming II Fall 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.

More Related