At the end of this chapter, you should be able to
Write C++ programs using classes and objects.
Understand concepts of constructors, destructors, and member data and functions.
Understand container classes and their usage.
Understand friend functions and inline functions.
Understand objects and dynamic memory usage with pointers and reference.
Understand this operator and static declarations and their usage.
We use class in C++ to define our own data type. A class is a derived data type like an array. The difference is that in an array you have single data type, while in a class data type you can have different data types. These different data types can be intrinsic data types such as int, float, etc. or derived or user-defined data types or functions or operators.
Object contains data types and functions and/or operators defined by class. So we can call object as an instance of a class. In other words, object is a variable of data type class. We can define an object as an independent entity that holds its own data and member functions. We can say that object tells us the data it holds and allowable operations on the object. A class therefore allows us to encapsulate member functions and member data into single entity called object.
Object-oriented programming involves writing programs that use classes. We create objects of different classes to solve the problem at hand and make objects communicate with each other through the member functions. In this chapter, we will learn how to solve problems by using classes and objects. We will also cover topics such as data hiding and abstraction and access privileges enjoyed by members of the class.
This chapter introduces the concepts of constructor and destructor. Variations in member functions such as friend functions and inline functions are discussed. Classes within a class, called container classes, are introduced. Different methods to use dynamic memory with objects using pointers and references are highlighted with their merits and demerits. This operator and static member functions and their relevance and usage are explained.
Look around your classroom. There are students all around. First of all, why have they been grouped together by your principal? Firstly, because they all share common interests, for example they would like to learn the language C++ or they would like to complete their PG or UG studies. In computer parlance, we can say that all students have the same functionality. That is why they can be grouped together. Figure 10.1 shows member functions and member data.
Figure 10.1 Class member functions and attributes
But notice that each student has his own individual attributes. Attributes mean own member data like height, weight, marks, attendance, etc. Also notice that there are about 60 students in your class each with his or her own attributes but common functionality. We can call 60 instances of object of Students class. Well so much background for analogy.
Class: A collection of objects. We can also define as an array of instances objects. But class can have member functions and member data. Here, unlike array, a class can have different data types as its elements.
Object: An object is an entity. That is, it can be felt and seen. Examples are student, pen, table, chair, etc. Therefore, an object is an independent entity that has its own member data and member functions.
Data Hiding/Data Abstraction. It is customary to declare all member data as private only. But the data declared as private is hidden and cannot be accessed by any one. This feature is called data hiding or data abstraction. But how can we get them? You can access this only through public member functions. There is no other way. It is comparable to the case where even the chief librarian of a university library cannot take home the books unless he uses the access card supplied by the library.
Public: Member functions and data if any declared as public can be accessed outside the class member functions.
Private: Member data declared as private can only be accessed within the class member functions and data is hidden from outside.
Protected: Member data and member functions declared as protected is private to outsiders and public to descendants of the class in inheritance relationship. You will learn more about this in the chapter on Inheritance.
Encapsulation: Binding together the member functions and member data with access specifiers like private, public, etc. into object by the class is called encapsulation.
Declaring a class: Use keyword class followed by brace brackets. Inside brace brackets we can include member data and member functions as shown below:
Example 10.1: Class Declaration
class Student
{ // constructors and member functions
public:
Student(){rollNo=50595,name=”Anand”;}
~Student(){}; // Default destructor
int GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { rollNo=n;}
// member data
private:
int rollNo;
};
Remember that an object is a variable of a class. Therefore, you can define the object just like you define a variable of a data type. In the example that follows, we create an ordinary object called std and 50 instances of class Student, as well as a pointer that creates an instance of Student on the Heap memory.
Example 10.2: Creation of an Object of a Class
Student std; // std is an object of class Student
Student std[50]; // There are 50 instances created for Student class
Student *ptr = new Student;// object on free store created
Once the object to a class is created, we can access the member functions and public member data of a class using dot(.)operator. We have learnt that member data is declared as private in C++ to achieve data hiding and abstraction. Hence, to access these private member data, we need to define public member functions. We call them public accessory functions. To draw an example from our life, you would have observed that books in your library can only be borrowed if you have the library cards. So is the case with the chief librarian; though he is the custodian of the books, he also needs library cards to take books home.
Example 10.3: Accessing Member Functions and Member Data
Student Std: // object is created
Std.SetRollNo(8345); // Sets roll no to 8345
cout<<Std.GetRollNo(); // displays roll number
In our next example, we will show all the concepts we have discussed so far through a class declaration called Polar. In this program, you will learn the concept of class and object, public accessory functions, etc. The class we will consider is vector in Cartesian form denoted by real number a and an imaginary component b. In Polar coordinates, the same can be represented by vector whose magnitude is r given by the formula r = √ ( a2 + b2 ), and the direction is given by θ = tan−( b / a ).
Our program will accept the real value a and imaginary component b through a public accessory function called GetData() and convert it to Polar form with magnitude r and angle θ through a public accessory function called ConvertToPolar() and display the result through a function called DisplayPolar(). A word or two about the way we will declare and define functions. If the function to be implemented is big code, then we would declare the prototype inside the class as
void ConvertToPolar(); // to convert to polar form and define the function code outside the class as shown below:
void Polar::ConvertToPolar()
{ double x=0.0;
r=sqrt(a*a + b*b);
x=atan(b/a); // x is in radians
t= (7.0/22.0)*180.0*x; // conversion to degrees .PI radians 180 degrees
}
Note that we have used the operator :: called scope resolution operator to inform the compiler to link up with function prototype declared within the class. Suppose the function to be implemented is one line or two lines only; we can then declare it as inline function as we have explained in Chapter 3.
inline double GetMag() const { return r ;}
There is also an easier option of including the code in class definition itself as we have shown: double GetMag() const { return r;}. Then compiler treats the function as inline automatically.
Example 10.4: Polarform.cpp Accessing Member Functions and Data of a Class
#include<iostream>
#include<cmath> // for maths related functions like sqrt,cos, tan, tan- etc.
using namespace std;
// Declaration of class called Polar
class Polar
{ // public accessory functions
public:
void GetData();
double GetReal() const { return a;} // returns real component a. const implies
// function can only return a, but it cannot alter
double GetImag() const { return b;}
double GetMag() const { return r;} // Two function GetMag() & GetTheeta
double GetTheeta() const { return t;} // for Polar form
void ConvertToPolar(); // to convert to polar form
void DisplayPolar(); // to display in polar and cartesian forms
// all member data is declared as private
private:
double r; // magnitude
double t; // angle theeta
double a; // real
double b; // imaginary
};
void Polar::GetData() // :: is called scope resolution operator. It is used since we are
// defining GetData() outside the class definition
{ cout<<”Enter Cartesian form Vector Details “<<endl;
cout<<” Enter real <a> : “;
cin >>a;
cout<<” Enter Imaginary <b> : “;
cin >>b;
r=0.0; t=0.0;
}
void Polar::DisplayPolar()
{ cout<<"
Inside the classes member function …"<<endl;
cout<< "
Vector in polar form using r and t directly: magnitude = "<<r
<<" Angle ="<<t<<endl; // We can directly use r and t because it is accessed
// inside the member function of the class thus equal to public
cout<< "
Vector in polar form using v1.GetMag() etc: magnitude = "<<GetMag()
<<" Angle ="<<GetTheeta()<<endl;
}
void Polar::ConvertToPolar()
{ double x=0.0;
r=sqrt(a*a + b*b);
x=atan(b/a); // x is in radians
t= (7.0/22.0)*180.0*x; // conversion to degrees .PI radians equals 180 degrees
}
void main()
{ Polar v1; // v1 is the object of Polar class
// obtain data for v1
v1.GetData(); // This is the way we will call member functions using object v1
// convert to Polar form
v1.ConvertToPolar();
cout<<"
Vectors in Polar form ….
"<<endl;
v1.DisplayPolar();
//cout<<"
Vector in polar form : magnitude & Angel = "<<v1.r<<v1.t<<endl;
//Error ‘r’ ‘t’: cannot access private member declared in class ‘Polar’. Commented out
cout<<"
Outside the class using v1.Getmag() etc…."<<endl;
cout<< "
Vector in polar form : magnitude = "<<v1.GetMag()
<<" Angle ="<<v1.GetTheeta()<<endl; // This is the correct way
}// end of
/*Output : Enter Cartesian form Vector Details
Enter real <a> : 3
Enter Imaginary <b> : 4
Vectors in Polar form form ….
Inside the class …
Vector in polar form using r and t directly: magnitude = 5 Angle =53.1087
Vector in polar form using v1.GetMag() etc: magnitude = 5 Angle =53.1087
Outside the class using v1.Getmag() etc….
Vector in polar form: magnitude = 5 Angle =53.1087
*/
A few points to remember
Inside member functions that is invoked by object you can access the private members directly. For example, in function DisplayPolar(), we have used cout<<r. This is ok.
But outside, like in main, we cannot use v1. r. We have to use public accessory function like v1.GetMag().
When you need to construct a house, you go to a specialist called architect or mason so that they make the house ready for occupation and usage. So is the case with Class. When a class is declared and an object is created, the constructor for the class is called. His job is to create the object, allocate memory for the data members and initialize them with initial values if supplied by the user.
The constructor will have the same name as that of the class but no return value. The constructor needs to be declared as public.
Student(){ } // Default constructor. No initial values. If you do not declare the default constructor, compiler automatically creates it. Hence, the term default constructor.
Overloading means the same function name but different types or different numbers of arguments. For example:
Student ( int n, char * p) { rollNo=n, name = p ; } is an overloaded constructor.
Destructor: When the program has ended there is a need to clear the memory resources allocated to it. This job is done by the destructor.
~Student(){ }; // Default destructor. The default destructor is called automatically when the program ends.
Copy Constructor: This is the second of the special constructors, the first being default constructor, created automatically, if you do not create on your own. It takes one argument i.e., the object to be copied. Look at the following declaration and definition:
Student( const Student & src) :rollNo(src.rollNo),name(src.name){}
The object is passed as constant reference so that it cannot be altered. Once declared, we can use it to copy the entire state of the object into new object.
Student std; // object is created
Student std1(std) ; // entire object std is copied on to std1
Let us attempt a problem to make our understanding of constructors and destructors clearer. In this program, you will learn the concept of class constructor overloaded constructor, copy constructor, default destructor, etc.
Example 10.5: classbasic2.cpp Constructors and Destructors of a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
// overloaded constructors
Student(){rollNo=0,name=”No Name”;}
Student(int n, char *p){rollNo=n,name=p;}
Student( const Student & src) // copy constructor
{ rollNo=src.GetRollNo();name=src.GetName();
cout<<” Copy Constructor….”<<endl;}
~Student(){}; // Default destructor
// public access functions
int GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { rollNo=n;}
char * GetName() const { return name;}
void SetName( char *p) { name=p;}
private:
int rollNo;
char *name; // pointer to name
};
void main()
{ Student std; // object created
cout<<”
Students rollNo set by Default constructor : “
<<std.GetRollNo()<<endl;
cout<<”
Students name set by Default constructor : “
<<std.GetName()<<endl;
//set roll no to 6060 and name to gautam.
//We have to use public accessory function to update private data
// Why ? Because we are not inside member function
std.SetRollNo(6060);
std.SetName(“Gautam”);
cout<<”
Students rollNo set by SetRollNo :”
<<std.GetRollNo()<<endl;
cout<<”
Students name set by SetName :”
<<std.GetName()<<endl;
// Now let us make use of overloaded constructor
Student std2(7070,”Ramesh”);
cout<<”
Students rollNo set by (int n, char *p) overloaded
constructor : “
<<std2.GetRollNo()<<endl;
cout<<”
Students name set by (int n, char *p) overloaded : “
<<std2.GetName()<<endl;
// Now let us use copy constructor and copy std2 onto std3
Student std3(std2); // std is copied on to new object called std3
cout<<”
Students(std3 rollNo) : “<<std3.GetRollNo()<<endl;
cout<<”
Students (std3 name) : “<<std3.GetName()<<endl;
}
/*Output : Students rollNo set by Default constructor : 0
Students name set by Default constructor : No Name
Students rollNo set by SetRollNo :6060
Students name set by SetName :Gautam
Student(std2.rollNo) set by (int n, char *p) overloaded constructor : 7070
Student(std2.name) name set by (int n, char *p) overloaded : Ramesh
Copy Constructors
Students(std3 rollNo) : 7070
Students (std3 name) : Ramesh*/
We have explained the salient features in the running program itself. Also take a final look at the public accessory functions we have used.
int GetRollNo() const { return rollNo;}
char * GetName() const { return name;}
When we do not want the function to alter value, we declare as constant as shown above. It will be an error if we write int GetRollNo() const { return rollNo++;}. Whereas in the functions shown below SetName can set, i.e. alter the name. Hence we have not declared as constant.
void SetName( char *p) { name=p;}
void SetRollNo ( int n) { rollNo=n;}
Look at the constructor declaration and definition we have declared and defined as
Student(int n, char *p){rollNo=n,name=p;} .
We can also declare and define it as
Student(int n, char *p) : rollNo(n), name(p) { } . We will be using this mode of declaration and definition in all our programs henceforth.
Due to data encapsulation and data hiding features of C++, only public member functions can access private data. We can also declare member functions to access private data, like we have been using GetData() in our programs, but it is cumbersome. Is there a method by which we be can access private data through a non-member function? Friend operator just achieves that. You can include a non-member inside a class by declaring it as friend.
For example, let us revisit the Polar class, declare friend functions for Multiplication and DisplayData() and see ourselves how the program gets simplified.
friend void DisplayPolar(Polar v); // to display in polar forms
friend Polar Multiply(Polar v1, Polar v2);
When defining these friend functions outside the class, there is no need to use scope resolution operator:: since friend functions are non-members.
void DisplayPolar(Polar v) // For a friend we do not use scope resolution operator
{// We can directly use r and t because DisplayPolar() is a friend function
cout<<”Magnitude”<<v.r<<” Angle =”<<v.t<<endl;
}
Polar Multiply(Polar v1, Polar v2)
{ Polar v3;
v3.r = v1.r * v2.r; // multiply the magnitudes
v3.t = v1.t+v2.t; // add the angles
return v3;
}
Also notice that though the functions are non-members, because they are friend functions we could use private data directly without using public accessory functions. What is then the cost paid for this simplification? You have given access to private data to a non-member function.
Example 10.6: classbasic3.cpp Friend Functions of a Class
/* In this program, you will learn the concept friend operator.
Two functions Multiply and DisplayData are declared as friend functions*/
#include<iostream>
#include<cmath> // for maths related functions like sqrt,cos, tan, tan- etc
using namespace std;
// Declaration of class called Polar
class Polar
{ //public accessory functions
public:
Polar():r(0.0),t(0.0){} // Default constructor
Polar( double f, double g):r(f),t(g){} // This is how we will declare
// constructor
~Polar(){} // Default Destructor
friend void DisplayPolar(Polar v); // to display in polar forms
friend Polar Multiply(Polar v1, Polar v2);
// all member data is declared as private
private:
double r; // magnitude
double t; // angle theeta
};
void DisplayPolar(Polar v) // For a friend we do not use scope resolution operator
{ // We can directly use r and t because DisplayPolar() is a friend function
cout<<”Magnitude”<<v.r<<” Angle =”<<v.t<<endl;
}
Polar Multiply(Polar v1, Polar v2)
{ Polar v3;
v3.r = v1.r * v2.r; // multiply the magnitudes
v3.t = v1.t+v2.t; // add the angles
return v3;
}
void main()
{Polar v1(5.0,53.0),v2(6.0,28.0),v3; // v1,v2,v3 is the object of Polar class
// multiply Polar Vectors v1 & v2 and store it in v3
v3=Multiply(v1,v2);
cout<<“
The Given Vectors in Polar form form ….
“<<endl;
cout<<“
Polar form vector v1 :“;
DisplayPolar(v1);
cout<<“
Polar form vector 2 v2 :“;
DisplayPolar(v2);
cout<<“
v3=v1*v2 in Polar Form :“;
DisplayPolar(v3);
}// end of main
/*Output :The Given Vectors in Polar form form ….
Polar form vector v1 :Magnitude5 Angle =53
Polar form vector 2 v2 :Magnitude6 Angle =28
v3=v1*v2 in Polar Form :Magnitude30 Angle =81*/
Container class is one of the techniques provided by C++ to achieve reusability of the code. Inheritance is another powerful feature for reusability which we will explore in the chapter on inheritance.
Container class means a class containing another class or more than one class. For example, a computer has a microprocessor in it. Further, a Student class can have a data member belonging to a string class. Then we would say that the string class is contained in the Student class. In other words, it can also be called composition or aggregation of string class. The advantage of containment is that we can use string class within Student class to store the details of name and address, etc., belonging to string class. Similarly, if we define a class called Date with day, month and year information, we can define object of Date within a class called Student and define Date-related data such as DOB, DOJ, etc.
Composition is a “has”-type of relation. For the example we have given above Student has name of string class and DOB of Date class.
Example 10.7: PointCircle.cpp An Example for Container Class, i.e. Class as a Data Member of Another Class. Constructors and Destructors of a Class
In this program, you will learn the concept of class within a class. That is, class as a data member of another class. We will define a class called Point and later declare this as a data member of class called Circle. We will then find the area of the circle.
#include<iostream>
#include<cmath>
using namespace std;
// Declare a class called Point
class Point
{ public:
Point(){} // default constructor
~Point(){} // default Destructor
float GetX() const { return x;}
void SetX(float f) { x=f;}
float GetY() const { return y;}
void SetY(float f) { y=f;}
private:
float x;
float y;
};
class Circle
{ // We will use user defined data called Point declared above
public:
Circle(float r, float p, float q );
~Circle(){} // default Destructor
Point GetCircleCenter() const { return CircleCenter;}
void SetCircleCenter(Point f) { CircleCenter=f;}
float GetCircleRadius() const { return CircleRadius;}
void SetCircleRadius(float f) { CircleRadius=f;}
float GetArea() const; // Fn to Find Area of the Circle
private:
float CircleRadius;
Point CircleCenter; // CircleCenter is a object of class Point
};
Circle::Circle(float r, float p, float q)
{
CircleCenter.SetX(p); // CircleCenter is object of Point
class
CircleCenter.SetY(q);
CircleRadius=r;
}
float Circle::GetArea() const{return (22.0/7.0)*CircleRadius*CircleRadius;}
void main()
{ Circle circ(3.0,1.5,2.5); // object is created with center(1.5,2.5)
and Radius=3.0
float area=circ.GetArea();
cout<<” area of the circle with radius = “<<circ.GetCircleRadius()
<<” Center(“<<circ.GetCircleCenter().GetX()
<<”,”<<circ.GetCircleCenter().GetY()<<”)=”<<area<<” Sq Units”<<endl;
}/*Output:
area of the circle with radius = 3 Center(1.5,2.5)=28.2857 Sq Units*/
Observe the cout statement we have used circ.GetCircleCenter() returns a Point data type. With this Point we have invoked GetX() and GetY().
cout<<” area of the circle with radius = “<<circ.GetCircleRadius()
<<” Center(“<<circ.GetCircleCenter().GetX()
<<”,”<<circ.GetCircleCenter().GetY()<<”)=”<<area<<” Sq Units”<<endl;
As another example consider a class called Student containing another class called Date.
Date class has date information as data members and public accessory function to set and get date fields such as day, month and year. Class Student declares variables belonging to class Date as shown below:
Date dateDOB;
Date dateDOJ;
Member functions to access these private variables are also declared. It may be noted that GetDOB() returns variable of type Date.
Date GetDOB() const { return dateDOB;}
Date GetDOJ() const { return dateDOJ;}
void SetDOB( Date dtb) { dateDOB=dtb;}
void SetDOJ( Date dtj) { dateDOJ=dtj;}
Example 10.8: DateStudent.cpp Student Class Contains Date Class. Constructors and Destructors of a Class
#include <iostream>
using namespace std;
class Date
{public:
Date ( int d=0, int m=0,int y=0) :dd(d),mm(m),yy(y) { } // constructor
void SetDate( int d, int m, int y){ dd=d,mm=m,yy=y;}
int GetDD() const { return dd;};
int GetMM() const { return mm;};
int GetYY() const { return yy;};
private:
int dd;
int mm;
int yy;
};
class Student
{ public:
Student();
~Student(){}; // Default destructor
int GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { rollNo=n;}
Date GetDOB() const { return dateDOB;}
Date GetDOJ() const { return dateDOJ;}
void SetDOB( Date dtb) { dateDOB=dtb;}
void SetDOJ( Date dtj) { dateDOJ=dtj;}
private:
int rollNo;
Date dateDOB;
Date dateDOJ;
};
Student::Student()
{ dateDOB.SetDate(14,11,54);
dateDOJ.SetDate(25,11,77);
rollNo=5050;
}
void main()
{ Student std; // object created
cout<<”
Students rollNo set by Default constructor : “<<std. GetRollNo()<<endl;
cout<<”
date of Birth :”;
cout<<std.GetDOB().GetDD()<<”/”<<std.GetDOB().GetMM()
<<”/”<<std.GetDOB().GetYY()<<endl;
cout<<”
date of Joining :”;
cout<<std.GetDOJ().GetDD()<<”/”<<std.GetDOJ().GetMM()
<<”/”<<std.GetDOJ().GetYY()<<endl;
}
/*Output : Students rollNo set by Default constructor : 5050
date of Birth :14/11/54
date of Joining : 25/11/77*/
Note that in cout statements in the main program, std.GetDOJ() returns data type of Date. Hence, with that variable we have further invoked GetMM().
We have learnt that Heap memory is dynamic memory and we can derive lot of advantage by using heap memory. The main advantage is the conservation of primary memory that arises as a result when we load objects, use and free the memory after use. In this way we can solve large complex problems. So naturally we have to equip ourselves with the skills for deploying objects onto heap memory. As an example, we can declare a class called Student. We can further declare a pointer to that class and create an object of Student on Heap Memory.
Student *ptr - new Student;// object on free store created
We use in direction operator -> to access the object functions and member data on the heap. : cout<<ptr->GetName()<<endl; // access member data from heap. After use we need to free the heap memory allocated. :delete ptr; // we need to delete and free dynamic memory. The following program makes things clear.
Example 10.9: objheap.cpp Creation of Object on to Heap Memory and Access Member Data. Constructors and Destructors of a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{public:
Student(){rollNo=50595,name=”Anand”;}
~Student(){}; // Default destructor
int GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { rollNo=n;}
char * GetName() const { return name;}
void SetName( char *p) { name=p;}
private:
int rollNo;
char *name;
};
void main()
{ Student std; // object created
Student *ptr = new Student;// object on free store created
cout<<”
Students rollNo set by Default constructor : “
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor : “
<<ptr->GetName()<<endl; // access member data from heap
//set roll no to 50
ptr->SetRollNo(6060);
ptr->SetName(“Gautam”);
cout<<”
Students rollNo set by SetRollNo :”
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor :”
<<ptr->GetName()<<endl;
//now delete the memory from heap
delete ptr; // we need to delete and free dynamic memory
}
/*output :Students rollNo set by Default constructor : 50595
Students name set by Default constructor : Anand
Students rollNo set by SetRollNo :6060
Students name set by Default constructor :Gautam*/
In the above example, we have created entire object on heap memory. We can also create the member data on heap memory and use indirection operator -> to access data members. Note that heap memory can be accessed only through the use of pointers. Therefore, our declaration of member data would be pointers
int *rollNo; // we have to use pointers as we want to use Heap Memory
char *name;
Our public accessory functions have to deal in pointers :
int GetRollNo() const { return *rollNo;}
Creation of object onto heap memory and access of members would be
Student *ptr = new Student;// object on heap created
cout<<ptr->GetRollNo()<<endl;
Example 10.10: objmemheap.cpp Creation of Object and Member Data on to Heap Memory and Access Member Data Using Indirection Operator -> Member Data. Constructors and Destructors of a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student();
~Student(){delete rollNo;delete name;} // Default destructor
int GetRollNo() const { return *rollNo;}
void SetRollNo ( int n) { *rollNo=n;}
char * GetName() const { return name;}
void SetName( char *p) { name=p;}
private:
int *rollNo; // we have to use pointers as we want to use Heap Memory
char *name;
};
Student::Student(){rollNo= new int(50);name= new char;name=”ramesh”;}
void main()
{ Student *ptr = new Student;// object on heap created
cout<<”
Students rollNo set by Default constructor : “
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor : “
<<ptr->GetName()<<endl;
//set roll no to 6060
ptr->SetRollNo(6060);
ptr->SetName(“Gautam”);
cout<<”
Students rollNo set by SetRollNo :”
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor :”
<<ptr->GetName()<<endl;
}//end of main
/*Output : Students rollNo set by Default constructor : 50
Students name set by Default constructor : ramesh
Students rollNo set by SetRollNo :6060
Students name set by Default constructor :Gautam*/
We are aware that memory management of C++ allocates separate memory area called code section for your code. Accordingly your function code will be in this area. But we also know that variables are stored either in stack area or in heap memory. When a function is invoked by an object, there is a need to link up function that is called with the objects data. Therefore, the object is forwarded to function as an argument to that function. But this argument, i.e. address of the object invoking the function, is not visible like ordinary formal arguments and it is hidden. This hidden address is called this pointer. It is called this because it points this object, i.e. object that has invoked the function.
In a function if you use this pointer explicitly then it refers to object member data. Note that we can get the same effect by using object instead. But note that this is a pointer and hence it has all the advantages of instant and fast access to object. Look at the program at Example 10.9 rewritten using this pointer.
Example 10.11: this.cpp Object on to Heap Memory Using this Pointer. Constructors and Destructors of a Class
#include<iostream>
using namespace std;
class Student
{ public:
Student(){rollNo=50595,name=”Anand”;}
~Student(){}; // Default destructor
int GetRollNo() const { return this->rollNo;}
void SetRollNo ( int n) { this->rollNo=n;}
char * GetName() const { return this->name;}
void SetName( char *p) { this->name=p;}
private:
int rollNo;
char *name;
};
void main()
{ Student std; // object created
Student *ptr = new Student;// object on free store created
cout<<”
Students rollNo set by Default constructor : “
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor : “
<<ptr->GetName()<<endl; // access member data from heap
//set roll no to 50
ptr->SetRollNo(6060);
ptr->SetName(“Gautam”);
cout<<”
Students rollNo set by SetRollNo :”
<<ptr->GetRollNo()<<endl;
cout<<”
Students name set by Default constructor :”
<<ptr->GetName()<<endl;
//now delete the memory from heap
delete ptr;
}
/*Output :Students rollNo set by Default constructor : 50595
Students name set by Default constructor : Anand
Students rollNo set by SetRollNo :6060
Students name set by Default constructor :Gautam*/
We are aware that pointers are addresses and provide us a fast access to memory and data manipulation. But there are issues of safe operation and data integrity. In Section 4.5.11, we have seen the use of pointers and effect of declaration of const pointer and constant data. In this section, we will study the effect and efficacy of using const declaration to pointers while working with objects. While manipulating objects stored in memory, a programmer faces several demanding situations such as data should not be altered. In such cases, we would have declared that data as constant.
The pointers to be constant i.e. they should not be reassigned. In such cases, we would declare as const pointers.
Thus, there are two combinations involved, i.e. Pointer type and Data Type. Each type can have two variations of data type: constant and normal. Therefore, we can have a total of four variations in declaration of variables. These are shown below:
Pointer to Object
// normal pointer & Normal object on heap
Student *ptr = new Student; // ptr is normal pointer on heap
Pointer to const Object
// conststudent is a pointer to constant Student
const Student *conststudent = new Student;
Const pointer to Object
//constptr is constant pointer to Student
Student * const constptr = new Student;
Const pointer to a const Object
//cptrcstd is a constant pointer to constant student
Const Student * const cptrstd = new Student;
In the example that follows we would demonstrate the methods and rules for deployment of different types of pointers and data type discussed above. When we use normal pointer to normal objects on heap, we can use Get() and Set() functions. Usage of Set() function implies that we can modify the objects data. Note that all Get () functions we would have declared Get() as const. so that it cannot alter the data values but it can only browse these values
Student *ptr = new Student;
int GetRollNo() const { return rollNo;} // only read . Function cannot alter data.
void SetGrade( char p) { grade=p;}
When we use pointer to constant object, we cannot use serGrade() functions as it sets data for grade. This is so because we have declared the object as constant.
// conststudent is a pointer to constant Student
const Student *conststudent = new Student;
ptr->SetGrade(‘C’); // NOT OK
When we use the third option, i.e. const pointer to constant object, pointer cannot be reassigned nor can the data be amended.
// const pointer to constant object
Const Student * const cptrstd = new Student;
int GetRollNo() const { return rollNo;} // Allowed
ptr->SetGrade(‘C’); // NOT OK
Example 10.12: ptrcdata.cpp To Demonstrate Use of Constant Pointer and Constant Object. Constructors and Destructors of a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student(){rollNo=50595,grade=’A’;}
~Student(){}; // Default destructor
int GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { rollNo=n;}
char GetGrade() const { return grade;}
void SetGrade( char p) { grade=p;}
private:
int rollNo;
char grade;
};
void main()
{ // ptr is normal pointer on heap
Student *ptr = new Student;
// conststudent is a pointer to constant Student
const Student *conststudent = new Student
//constptr is constant pointer to Student
Student * const constptr = new Student;
const Student * const cptr = new Student;
cout<<”explore the possibilities with normal heap pointer *ptr”<<endl;
cout<<”
Students rollNo set by Default constructor : “
<<ptr->GetRollNo()<<endl;
cout<<”
Students Grade set by Default constructor : “
<<ptr->GetGrade()<<endl;
//set roll no to 50 and Garde to B
ptr->SetRollNo(6060);
ptr->SetGrade(‘B’);
cout<<“
Students rollNo set by SetRollNo() function :“
<<ptr->GetRollNo()<<endl;
cout<<“
Students name set by setgrade() function:“
<<ptr->GetGrade()<<endl;
cout<<“
explore the possibilities with pointer *conststudentto“<<endl;
cout<<“ constant object Student“<<endl;
cout << ”
Display Grade …“<<conststudent ->GetGrade()<<endl;
//conststudent->SetGrade(‘C’); //NOT OK.
/*const pointer calling non-const fn . This is error. conststudent is a pointer to constant Student, whereas SetGrade is a non-constant function and can change value. Hence we cannot use. You will observe error C2662: ‘SetGrade’ : cannot convert ‘this‘ pointer from ‘const class Student‘ to ‘class Student &‘ Hence it
shown in comments */
// constant pointer to constant data object
cout<<“
explore the possibilities with const pointer *cptr to“<<endl;
cout<<“ constant object Student“<<endl;
cout<<“
cptr-> GetGrade()..“<<cptr->GetGrade()<<endl;//ok
//cout<<“
cptr->SetGrade(‘C‘)..“<<cptr->SetGrade(‘C‘)<<endl;//not ok
//now delete the memory from heap
delete ptr;
delete cptr;
delete conststudent;
}
/*Output :explore the possibilities with normal heap pointer *ptr
Students rollNo set by Default constructor : 50595
Students Grade set by Default constructor : A
Students rollNo set by SetRollNo() function :6060
Students name set by setgrade() function:B
explore the possibilities with pointer *conststudentto
constant object Student
Display Grade …A
explore the possibilities with const pointer *cptr to
constant object Student
cptr-> GetGrade()..A/*
So why do we use constant pointers?
We do so because constant pointers cannot call non-constant member functions, i.e. member functions that can alter member data. So our member data is safe. While const pointer to constant object is possible, it is tedious and cumbersome. Instead, the same can be achieved more elegantly if we use reference. Remember, insinuations wherein we cannot reassign pointers, reference is a better alternative.
We are aware that normal variables can be passed to and from function with pass by value and pass by reference. We have also learnt in Chapter 6 on pointers that passing by reference involves passing either by pointer or reference (address). Objects, because they occupy good amount of memory, have to be passed as call by reference. More so because if you use call by value, a copy is made when an object is passed on to function and another copy of the object is made when function returns an object to calling function. What a waste of precious memory! Not only that, other resources are also employed such as calling a copy constructor and destructor. Your program is busy doing these unnecessary jobs when you use call by value.
In our next example, we will study the implications of call by value, call by reference on objects calling functions. For this, we would declare three different functions outside the class and show the costs and efficiencies involved in different modes of invoking the function.
Student FindGrade( Student std); //call by value
Student * FindGrPtr(Student *std); // call by ref using pointers
Student & FindGrRef (Student &std); // call by reference using reference
Also note that we have declared all the member data as pointers.
int *rollNo; float *total; char *grade;
Constructor allocates memory in Heap memory with new operator as shown below
Student::Student(int n, float t, char c)
{ cout<<”
This is Student(int,float,char) constructor…
”;
rollNo= new int(n);
grade= new char(c);
total=new float(t);
}
Destructor is called whenever we have to destroy the object. But remember, our data members like rollNo, grade, and total are also residing on the heap memory. Therefore, we have to explicitly delete them
~Student(){cout<<”
This is destructor..
”;
delete rollNo;delete grade;delete total;
} // destructor
Copy constructor gets data items from src and allocates space on heap with new operator.
Student::Student(Student &src) // src means source
{ cout<<”This is copy constructor..
”;
rollNo=new int(*(src.rollNo));
grade=new char(*(src.grade));
total=new float(*(src.total));
}
src is the name we use to denote that it is a source file. Generally left-hand side is reserved for current object, i.e. target and source is placed on the right-hand side. We are creating data members on heap memory. Therefore, rollNo and grade and total have been allocated space using new operator. Note that as we are within member function, i.e. constructor, we could use *src.rollNo directly without going through public accessory function ( *src.GetRollNo())
We have created an object of Student
Student std; // an object made .Therefore, constructor will be called
As our data members are pointers, cout statements in the main program are
cout<<”
RollNO:”<<*(std.GetRollNo())<<” ”
<<“Total:“<<*(std.GetTotal())<<endl;
When we use call by value by statement FindGrade(std); copy constructors are invoked twice: once for forwarding the object to function and once for return from the function. Normally changes made in the function are not reflected in the calling function in case of call by value. But in this case we are returning the object to called function by statement : return std; hence grade changes are reflected in the main program.
A call to a function FindGrPtr( Student *std) means we are resorting to pass by reference using pointer. In this case, copy constructors and destructors are not involved at all . This means that primary memory and computer time is conserved. As this is pass by reference using pointer, value set by local variable(grade) is reflected in the main. As we are using pointer to object and data members are all pointers we have to use indirection operator to access data members : cout<<*(std2->GetGrade())
Student *std2=new Student(); // std2 is created on heap .
std2=FindGrPtr(std2); // pass by reference pointer method
cout<<”
Students Grade set by FindGrPtr() : “
<<*(std2->GetGrade())<<endl; //pass by ref ptr
A call to a function Student & FindGrRef (Student &std); means that we are resorting to pass by reference using & operator. In this case, copy constructors and destructors are not involved at all. This means that primary memory and computer time is conserved. As this is pass by reference using address, value set by local variable(grade) is reflected in the main.
Student *std2=new Student(); // std2 is created on heap .
std2=FindGrPtr(std2); // pass by reference pointer method
cout<<”
Students Grade set by FindGrPtr() : “
<<*(std2->GetGrade())<<endl; //pass by ref ptr
Example 10.13: refptr.cpp To Demonstrate Passing of Objects by Reference and Pass by Pointers and Pass by Value. Constructors and Destructors of a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student();
Student(int n, float t, char c);
Student(Student &);
~Student(){cout<<”
This is destructor..
”;
delete rollNo;delete grade;delete total;} // Default destructor
int *GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { *rollNo=n;}
char *GetGrade() const { return grade;}
void SetGrade( char p) { *grade=p;}
float * GetTotal() const { return total;}
void SetTotal( float t) { *total=t;}
private:
int *rollNo;
float *total;
char *grade;
};
Student::Student()
{ cout<<"
This is default constructor…
";
rollNo= new int;
grade= new char(‘D’); // default Grade
total=new float;
}
Student::Student(int n, float t, char c)
{ cout<<"
This is Student(int,float,char) constructor…
";
rollNo= new int(n);
grade= new char(c);
total=new float(t);
}
Student::Student(Student &src) // src means source
{ cout<<"This is copy constructor..
";
rollNo=new int(*(src.rollNo));
grade=new char(*(src.grade));
total=new float(*(src.total));
}
// Three non-member functions
Student FindGrade( Student std); //call by value
Student * FindGrPtr(Student *std); // call by ref using pointers
Student & FindGrRef (Student &std); // call by reference using reference
void main()
{ cout<<"
create an ordinary object called std for Student class..
";
Student std; // an object made. Therefore constructor will be called Student *std2=new Student(); // std2 is created on heap.
std.SetRollNo(50);
std.SetTotal(99.0);
cout<<”
RollNO:”<<*(std.GetRollNo())<<” ”
<<”Total:”<<*(std.GetTotal())<<endl;
// now call a function FindGrade(Student std)
// See how copy constructors are involved one for forward copy and one for return
// see also how value set by local variable(grade =A) is reflected in the main
// because you are returning the object by value using statement return std
// you should also see two destructors
FindGrade(std);
cout<<”
Students Grade set by FindGrade() :”
<<*(std.GetGrade())<<endl;
// now call a functon FindGrPtr( Student *std)
// See how copy constructors and destructors are not involved at all
// see also how value set by local variable(grade) is reflected in the main
std2=FindGrPtr(std2); // pass by reference pointer method
cout<<”
Students Grade set by FindGrPtr() : “
<<*(std2->GetGrade())<<endl; //pass by ref ptr
// now call a function FindGrref(Student & std)
// See how copy constructors and destructors are not involved at all
// see also how value set by local variable(grade) is reflected in the main
FindGrRef(std); // pass by reference reference method
cout<<”
Students Grade set by FindGrRef() :”
<<*(std.GetGrade())<<endl; // pass by reference - reference */
}// end of main
// Define functions
Student FindGrade( Student std)
{ cout<<”
FindGrade( Student std) function …
”;
cout<<”
Students Grade set by FindGrade() :
”;
std.SetGrade(‘A’);
cout<<”
inside FindGrade..:”<<*(std.GetGrade())<<endl;
return std;
}
Student * FindGrPtr(Student *std)
{ std->SetGrade(‘B’);
cout<<”
Inside FindGrPtr() : “<<*(std->GetGrade())<<endl;
return std;
}
Student & FindGrRef ( Student &std)
{ std.SetGrade(‘C’);
cout<<”
Inside FindGrRef() : “<<*(std.GetGrade())<<endl;
return std;
}
/*Output :This is copy constructor..
FindGrade( Student std) function …
Students Grade set by FindGrade() :
inside FindGrade..:A
This is copy constructor..
This is destructor..
This is destructor..
Students Grade set by FindGrade() :D
Inside FindGrPtr() : B
Students Grade set by FindGrPtr() : B
Inside FindGrRef() : C
Students Grade set by FindGrRef() :C
This is destructor..*/
Recall that in the above example function Student * FindGrPtr(Student *std); receives the object by reference using pointers. It also amends the Grade. What if we want the function to receive the object by reference because of efficiency and yet should not be able to change the values. Clearly the solution is to pass a constant pointer to Student and thus FindGrPtr () not amend the object. This is because of the stipulation that a constant pointer can only call a constant function and constant function by definition cannot alter the values.
const Student * const FindGrPtr(const Student * const std);
const Student & FindGrRef (const Student &std);
Call by ref using constant pointers and also constant object. The second call involves call by reference using & operator and also constant object Student. Look at the program presented below to understand the concepts involved better.
Example 10.14: constrefptr.cpp To Demonstrate Constant Pointer and Constant Reference and Constant Object. Constructors and Destructors of a Class
//constptrref.cpp
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student();
Student(int n, float t, char c);
Student(Student &);
~Student(){cout<<”
This is destructor..
”;
delete rollNo;delete grade;delete total;} // Default destructor
int *GetRollNo() const { return rollNo;}
void SetRollNo ( int n) { *rollNo=n;}
char *GetGrade() const { return grade;}
void SetGrade( char p) { *grade=p;}
float * GetTotal() const { return total;}
void SetTotal( float t) { *total=t;}
private:
int *rollNo;
float *total;
char *grade;
};
Student::Student()
{ cout<<"
This is default constructor…
";
rollNo= new int;
grade= new char(‘D’); // default Grade
total=new float;
}
Student::Student(int n, float t, char c)
{ cout<<"
This is Student(int,float,char) constructor…
";
rollNo= new int(n);
grade= new char(c);
total=new float(t);
}
Student::Student(Student &src) // src means source
{
cout<<"This is copy constructor..
";
rollNo=new int(*(src.rollNo));
grade=new char(*(src.grade));
total=new float(*(src.total));
}
const Student * const FindGrPtr(const Student * const std);
const Student & FindGrRef (const Student &std);
void main()
{ cout<<"
create an ordinary objet called std for Student class..
";
Student std; // an object made . Constructor will be called
std.SetRollNo(50);
std.SetTotal(99.0);
cout<<"
RollNO :"<<*std.GetRollNo()<<" "<<"Total :"
<<*std.GetTotal()
<<"Grade : "<<*std.GetGrade() <<endl;
// now call const Student * const FindGrPtr(const Student * const std);
FindGrPtr(& std); // pass by reference pointer method
cout<<"
Students Grade after return from FindGrPtr() : "
<<*std.GetGrade()<<endl;
// now call a function : const Student & FindGrRef (const Student &std);
FindGrRef(std); // pass by reference reference method
cout<<"
Students Grade after returning from FindGrRef() :"
<<*std.GetGrade()<<endl;
}// end of main
const Student * const FindGrPtr(const Student * const std)
{ //std->SetGrade(‘B’); // clearly error constant pointer to constant obj
cout<<"
Inside FindGrPtr() : "<<*std->GetGrade()<<endl;
return std;
}
const Student & FindGrRef ( const Student & std)
{ //std.SetGrade(‘C’); // clearly error . student is a constant obj
cout<<"
Inside FindGrRef() : "<<*std.GetGrade()<<endl;
return std;
}
/*Output :create an ordinary objet called std for Student class..
This is default constructor…
RollNO :50 Total :99Grade : D
Inside FindGrPtr() : D
Students Grade after return from FindGrPtr() : D
Inside FindGrRef() : D
Students Grade after returning from FindGrRef() :D
This is destructor..*/
Note that in cout statements in main() and functions std.GetGrade() return a pointer of character type as per our function prototype declaration. Hence we have used dereference operator * : *std.GetGrade() or *std->GetGrade();
Recall our definition of encapsulation. It is binding of member data and member functions into a single entity by class. A class has access tights defined for its data members and enjoys the benefits of protective and security features like data hiding, abstraction, etc. Further you are aware that member data is exclusive to an instance of object. For example, Student Hari cannot share the data of Student Shiva.
If we want a single data item to be available to all instances of the class, one way is to declare it as a global variable. But in that case we would lose all advantages of being a member of a class like data security, access privileges, etc.
For example, if we want a variable called count to keep track of the number of objects being created and the number of objects being deleted, how do we declare such a variable?
If we declare it in global section, we would lose benefits of being within the class. But if we declare it within the class, it becomes part of the class and hence belongs to an instance of class and cannot be shared amongst objects. Then how can the variable keep track of the number of objects being created?
Variable declared as static, though resides inside a class; it can keep track of the objects being created or destroyed. This means that it is accessible to all instances of the class. We can say that static data belongs to class and not to an object.
class Student
{ public:
Student(){count++;}
~Student(){count--;} // Default destructor
static int count; // static data member declared as public
};
Therefore, to give access to all instances of the class declare the variable as static. It does not belong to class and is not initialized along with member data when the object is created. Also note that no memory resources are allocated by static declaration made inside the class. Hence, the static variables are required to be declared globally outside the class and then only resources are allocated.
int Student::count=0; // mandatory to declare in global section
In the example shown below, we have instantiated four objects, two outside the controlling braces and two within the controlling braces. We can use static variable count with any one of the objects to get the count of objects created:
Student a,b;
{ Student ramesh, suresh;
cout<<”ramesh.count”<<ramesh.count<<” ”<<”suresh.count”
}
The life of variables is till the controlling brace brackets. Hence we would get count as 2 when we check the count after the controlling braces.
Example 10.15: static1.cpp To Demonstrate Usage of Static Data Within a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student(){count++;}
~Student(){count--;} // Default destructor
static int count; // static data member declared as public
};
int Student::count=0; // mandatory to declare in global section
void main()
{
{ Student a,b; // two objects have been created
Student ramesh, suresh;
cout<<”ramesh.count”<<ramesh.count<<” ”<<”suresh.count”
<<suresh.count<<endl;
cout<<”The above result shows that static variable is available to ramesh & suresh”;
}
cout<<”
after brace brackets :”<<endl;
cout<<”a.count=”<<a.count<<endl;
Student w,x,y,z;
cout<<”
after creation of w x y z :”<<endl;
cout<<”a.count”<<a.count<<” ”<<”b.count”<<b.count<<endl;
}
/*Output :ramesh.count4 suresh.count4
The above result shows that static variable is available to ramesh & suresh
after brace brackets :
a.count=2
after creation of w x y z :
a.count6 b.count6*/
What happens if you declare the static data as private? Quite simple. By now you must have understood that the only way to access a private data is through a public accessory function. Code is reproduced for static data type that is declared as private.
Example 10.16: static2.cpp To Demonstrate Usage of Private Static Data Within a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{public:
Student(){count++;}
~Student(){count--;} // Default destructor
int GetCount() const { return count;}// public function to access static private data
private:
static int count; // static data member declared as private
};
int Student::count=0; // mandatory to declare in global section
void main()
{ Student a,b;
{ Student ramesh, suresh;
cout<<”ramesh.count”<<ramesh.GetCount()<<” ”<<”suresh.count”
<<suresh.GetCount()<<endl;
cout<<”The above result shows that static variable is available to ramesh & suresh”;
}
cout<<”
after brace brackets :”<<endl;
cout<<”a.count=”<<a.GetCount()<<endl; // because static private declaration
Student w,x,y,z;
cout<<”
after creation of w x y z :”<<endl;
cout<<”Student.count”<<x.GetCount()<<” ”<<”y.count”<<y.GetCount()<<endl;
}
/*Output :ramesh.count4 suresh.count4
The above result shows that static variable is available to ramesh & suresh
after brace brackets :
a.count=2
after creation of w x y z :
Student.count :6 y.count : 6*/
The data members declared as static is available for all instances of the class. That is why we could use x.GetCount() and y.GetCount() in the above program. Therefore, it really does not matter which object calls the function that displays static data.
To make it independent of object we need to define the member function as static.
static int GetCount() { return count;}// public function to access static private
cout<<Student::GetCount() // you can call static function without object
// by using class name and scope resolution operator
The next program illustrates the static function concept and usage.
Example 10.17: static3.cpp To Demonstrate Usage of Static Member Functions Within a Class
#include<iostream>
using namespace std;
//declare a class called Student
class Student
{ public:
Student(){count++;}
~Student(){count--;} // Default destructor
static int GetCount() { return count;}// public function to access static private data
private:
static int count; // static data member declared as public
};
int Student::count=0; // mandatory to declare in global section
void main()
{ Student a,b;
{ Student ramesh, suresh;
cout<<”Student.count”<<Student::GetCount()<<endl;
}
cout<<”
after brace brackets :”<<endl;
cout<<”Student.count=”<<Student::GetCount()<<endl;
Student w,x,y,z;
cout<<”
after creation of w x y z :”<<endl;
cout<<”Student count :”<<Student::GetCount()<<endl;
}
/*Output : Student.count4
after brace brackets :
Student.count=2
after creation of w x y z :
Student count :6
*/
3.15.231.194