330 likes | 481 Views
CHAPTER 15 POINTERS, CLASSES, AND VIRTUAL FUNCTIONS. In this chapter, you will: Learn about the pointer data type and pointer variables Explore how to declare and manipulate pointer variables Learn about the address of operator and the dereferencing operator Discover dynamic variables
E N D
In this chapter, you will: • Learn about the pointer data type and pointer variables • Explore how to declare and manipulate pointer variables • Learn about the address of operator and the dereferencing operator • Discover dynamic variables • Explore how to use the newand deleteoperators to manipulate dynamic variables • Learn about pointer arithmetic • Discover dynamic arrays • Become aware of the shallow and deep copies of data • Discover the peculiarities of classes with pointer data members • Examine the relationship between the address of operator and classes
THE POINTER DATA TYPE AND POINTER VARIABLES • Pointer variable: A variable whose content is an address— (that is, a memory address). int *p; char *ch; • The statement int *p; is equivalent to int* p; is equivalent to int * p; • The character * can appear anywhere between the data type name and the variable name. int* p, q; • Only p is the pointer variable, not q. Here q is an int variable. • To avoid confusion, we prefer to attach the character * to the variable name. int *p, q; • The following statement declares both p and q to be pointer variables of the type int. int *p, *q; THE ADDRESS OF OPERATOR (&) • In C++, the ampersand, &, called the address of operator, is a unary operator that returns the address of its operand. int x; int *p; p = &x; assigns the address of x to p. That is, x and the value of p refers to the same memory location.
THE DEREFERENCING OPERATOR (*) • C++ also uses * as a unary operator. • When used as a unary operator, *, commonly referred to as the dereferencing operator or indirection operator, refers to the object to which its operand (that is, a pointer) points int x = 25; int *p; p = &x; //store the address of x in p cout<<*p<<endl; //prints the value of x. Also the statement *p = 55; //will store 55 in the memory location pointed to by p, that is, in x. int *p; int num= 78; • p is a pointer variable of the type int and num is a variable of type int.
p = # *p = 24;
Example 15-2 #include <iostream> using namespace std; int main() { int *p; int x = 37; cout<<"Line 1: x = "<<x<<endl; //Line 1 p = &x; //Line 2 cout<<"Line 3: *p = "<<*p <<", x = "<<x<<endl; //Line 3 *p = 58; //Line 4 cout<<"Line 5: *p = "<<*p <<", x = "<<x<<endl; //Line 5 cout<<"Line 6: Address of p = "<<&p<<endl; //Line 6 cout<<"Line 7: Value of p = "<<p<<endl; //Line 7 cout<<"Line 8: Value of the memory location " <<"pointed to by *p = "<<*p<<endl; //Line 8 cout<<"Line 9: Address of x = "<<&x<<endl; //Line 9 cout<<"Line 10: Value of x = "<<x<<endl; //Line 10 return 0; } Line 1: x = 37 Line 3: *p = 37, x = 37 Line 5: *p = 58, x = 58 Line 6: Address of p = 006BFDF4 Line 7: Value of p = 006BFDF0 Line 8: Value of the memory location pointed to by *p = 58 Line 9: Address of x = 006BFDF0 Line 10: Value of x = 58
CLASSES, STRUCTS, AND POINTER VARIABLES • You can also declare pointers to other data types, such as classes and structs. struct studentType { char name[26]; double gpa; int ssn; char grade; }; studentType student; studentType* studentPtr; studentPtr = &student; student.gpa = 3.9; (*studentPtr).gpa = 3.9; store 3.9 in the component gpa of the object student. • Since dot has a higher precedence than the dereferencing operator, parentheses are important. • C++ provides another operator, called the member access operator arrow, ->. • The syntax for accessing a class (struct) member using the operator -> is pointerVariableName->classMemberName • The following statements are equivalent. (*studentPtr).gpa = 3.9; studentPtr->gpa = 3.9;
Output x = 5 Example 15-3 class classExample { public: void setX(int a); void print() const; private: int x; }; void classExample::setX(int a) { x = a; } void classExample::print() const { cout<<"x = "<<x<<endl; } int main() { classExample *cExpPtr; //Line 1 classExample cExpObject; //Line 2 cExpPtr = &cExpObject; //Line 3 cExpPtr->setX(5); //Line 4 cExpPtr->print(); //Line 5 return 0; }
INITIALIZING POINTER VARIABLES p = NULL; p = 0; DYNAMIC VARIABLES • Variables that are created during program execution are called dynamic variables. • With the help of pointers, C++ creates dynamic variables. • C++ provides two operators, new and delete, to create and destroy dynamic variables, respectively. int *p; char *q; int x; • The statement p = &x; stores the address of x in p. No new memory is allocated. p = newint; creates a variable during execution somewhere in the memory, and stores the address of the allocated memory in p. • The allocated memory is accessed via pointer dereferencing, *p. q = newchar[16]; creates an array of 16 components of the type char and stores the base address in q.
int *p; //p is a pointer of the type int char *name; //name is a pointer of the type char string *str; //str is a pointer of the type string p = newint; *p = 28; //stores 28 in the allocated memory name = newchar[5]; strcpy(name, "John"); //stores John in name str = new string; //allocates memory of the type string and stores //the address of theallocated memory in str *str = "Sunny Day"; //stores the string "Sunny Day" in memory pointed to by str The C++ operator delete is used to destroy dynamic variables. delete p; //to deallocate memory referenced by the pointer p delete [] name; //to deallocate a dynamically created array pointed by name
OPERATIONS ON POINTER VARIABLES • Assignment : Value of one pointer variable can be assigned to another pointer variable of the same type. int *p, *q; p = q; copies the value of q into p,which means p and q is pointing to the same Memory location. • Relational operations :Two pointer variables of the same type can be compared for equality, and so on. p == q // evaluates to true if both p and q have the same value. p != q // evaluates to true, if both p and q point to different memory locations
OPERATIONS ON POINTER VARIABLES • Some limited arithmetic operations. int *p; double *q; char *chPtr; studentType *stdPtr; //studentType data type is as defined before • The memory allocated for an int variable is 4 bytes, a double variable is 8 bytes, and a char variable is 1 byte. The memory allocated for a variable of the type studentType is 39 bytes. p++; or p = p+1; //increments the value of p by 4 bytes. q++; chPtr++; //increment the value of q by 8 bytes and the value of chPtr by 1 byte stdPtr++; //increments the value of stdPtr by 39 bytes. p = p + 2; //increments the value of p by 8 bytes.
DYNAMIC ARRAYS • An array created during the execution of a program is called a dynamic array. • To create a dynamic array, we use the second form of the new operator. int *p; //declares p to be a pointer variable of the type int. int arraySize; cout<<"Enter array size: "; cin>>arraySize; // if user enter 10, arraySize will be 10 p = newint[arraySize]; allocates 10 contiguous memory locations, each of the type int, and stores the address of the first memory location into p. *p = 25; // stores 25 into first memory location. p++; *p = 35; // stores 35 into the second memory location. • C++ allows us to use array notation to access these memory locations. p[0] = 25; p[1] = 35; for(j = 0; j < 10; j++) p[j] = 0;
int list[10]; • list is the array name and also a constant pointer that point the first array component. • Any attempt to use increment or decrement operation on list will result in compile time error. • If p is a pointer variable of the typeint, then the statement int *p; p = list; copies the address value of list into p. We are allowed to perform increment and decrement operations on p. for(j = 0; j < 10; j++) p[j] = 0; for(j = 0; j < 10; j++) *(p + j) = 0; for(j = 0; j < 10; j++) list[j] = 0; for(j = 0; j < 10; j++) *(list + j) = 0; for(p = list; p < list + 10; p++) *p = 0;
Functions and Pointers • A pointer variable can be passed as a parameter to a function either by value or by reference. • In C++, to make a pointer a reference parameter in a function heading, * appears before the & between the data type name and the identifier. void example(int* &p, double *q) { . . . } • Both p and q are pointers. The parameter p is a reference parameter; the parameter q is a value parameter. Pointers and Function Return Value • In C++, a function can return a value of the type pointer. • The return type of the function int* testExp(...) { . . . } is a pointer of the type int.
int *first; int *second; first = newint[10]; In a shallow copy, two or more pointers of the same type point to the same memory; that is, they point to the same data. second = first; delete [] second;
second = newint[10]; for(int j = 0; j < 10; j++) second[j] = first[j]; • In a deep copy, two or more pointers have their own data.
pointerDataClass::~pointerDataClass() • { • delete [] p; • } • class pointerDataClass • { • public: • ~pointerDataClass(); • ... • private: • int x; • int lenP; • int *p; • }; CLASSES AND POINTERS: SOME PECULIARITIES class pointerDataClass { public: ... private: int x; int lenP; int *p; }; pointerDataClass objectOne; pointerDataClass objectTwo;
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? • If a class has a destructor, the destructor automatically executes whenever a class object goes out of scope. • We can deallocate memory that was created by p the necessary code in the destructor to ensure that when objectOne goes out of scope, the memory created by the pointer is deallocated.
The Assignment Operator objectTwo = objectOne; • If objectTwo.p deallocates the memory space to which it points, objectOne.p would become invalid.
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. (chap. 16) • Once the assignment operator is properly overloaded, both the objects objectOne and objectTwo have their own data, as shown in Figure 15-19. • The Copy Constructor • Consider the following statement: pointerDataClass objectThree(objectOne); • The object objectThree is being declared and is also being initialized by using the value of objectOne. • This initialization is called the default member-wise initialization. • The default member-wise initialization is due to the constructor, called the copy constructor (provided by the compiler.) • This default initialization would lead to a shallow copying of the data.
void destroyList(pointerDataClass paramObject); destroyList(objectOne); pointerDataClass has pointer data members p: • Call by value would create a copy of the actual parameter into the formal parameter, and the default member-wise copying of data is allowed, it would lead to shallow copying of 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.
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.
Example 15-5 ptrDataClass.h 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 }; 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; }
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 = newint[lenP]; } pointerDataClass::~pointerDataClass() { delete [] p; } //copy constructor pointerDataClass::pointerDataClass (const pointerDataClass& otherObject) { x = otherObject.x; lenP = otherObject.lenP; p = newint[lenP]; for(int i = 0; i < lenP; i++) p[i] = otherObject.p[i]; }
#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; 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 oneis:"<<endl; //Line 17 one.print(); //Line 18 return 0; //Line 19 }
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 }
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 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
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.
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
//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; }
//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
#ifndef H_testAdd #define H_testAdd class testAddress { public: void setX(int); void printX() const; constint& addressOfX(); //this function returnsthe //address of theprivate data //member private: int x; }; #endif constint& testAddress::addressOfX() { return x; } 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 if we attempt to change the content of x through the alias.