Lecture
No.09
Review
Copy Constructor
Copy
constructors are used when:
Initializing
an object at the time of creation (we want to create an object with state of a
pre existing object)
When
an object is passed by value to a function (As you know temporary copy of
object is created on stack so we need copy constructor to create that temporary
object with the state of actual object being passed).
Example
void
func1(Student student){
…
}
int
main(){
Student studentA;
Student studentB = studentA;
func1(studentA);
}
|
Student::Student(
const Student &obj){
/*copying values to newly created
object*/
rollNo = obj.rollNo;
name = obj.name;
GPA = obj.GPA;
}
|
As
was the case with default constructor compiler also generates copy constructor
by itself however we can override that copy constructor by writing our own copy
constructor as shown in example below,
As
was the case with default constructor compiler also generates copy constructor
by itself however we can override that copy constructor by writing our own copy
constructor as shown in example below,
#include
<iostream>
using
namespace std;
class
Student{
int rollNo;
public:
Student(){
rollNo = 0;
cout<<”I am
default constructor of Student class…\n”;
}
Student(const
Student &obj){
cout<<”I am copy constructor of
Student class\n”;
rollNo = obj.rollNo;
}
};
int
main()
{
Student aStudent;
/*default constructor is
implicitly called at this point*/
Student bStudent = aStudent;
/*copy constructor is implicitly
called at this point*/
system(“pause”);
return
0;
}
|
When
we initialize one object with another then the compiler copies state of one
object to the other using copy constructor by assigning data member values of
previous object to newly created object. This kind of copying is called shallow
copying.
Shallow copy using
default Copy Constructor (Syntax)
Student::Student( const Student & obj ){
rollNo = obj.rollNo;
name = obj.name;
GPA = obj.GPA;
}
This
kind of copying is called shallow copying
Example
Student
studentA;
Student
studentB = studentA; /*Shallow copy:
compiler will use copy constructor to assign studentA values to newly created
object studentB*/
Example
Student
studentA(“Ahmad”);
Student
studentB = studentA;
#include
<iostream>
using
namespace std;
class
Student{
char * name;
int rollNo;
public:
Student(char * aName, int
arollNo){
name = new char[strlen(aName)+1];
strcpy(name,aName);
rollNo = arollNo;
}
Student(const Student &obj){
name = obj.name;
rollNo = obj.rollNo;
}
void showName(){
cout<<name<<endl;
}
~Student(){
delete []name;
}
};
int
main()
{
Student
studentA("AHMAD",1);
Student studentB = studentA;
/*copy constructor is implicitly
called at this point*/
studentA.showName();
studentB.showName();
system("pause");
return 0;
}
|
Shallow
copy works fine if our class doesn’t include dynamic memory allocation but in
case of dynamic memory allocation it leads to dangling pointer problem as
explained below.
Problem is Shallow
Copy
Student
class data member name of char * type is added to store the name of student and
it is using dynamic memory according to the length of name entered by
user for student.
Student
class data member name (char *) of object studentB is also pointing to memory
allocated for datamember name of object studentA, due to this there may
be two kinds of problems.
Suppose
we delete first object studentA for some reason then its destructor will also
free memory allocated by it hence memory area containing name “AHMAD” will also
be freed and will be given to some other application by operating system, but
studentB member name is still pointing to that area so issue of “Dangling
Pointer” [Pointer pointing to incorrect memory location] will arose.
Same
will happen if object studentB is deleted then studentA object data member name
will become dangling pointer. This has been explained below,
Let
us change code in main to make our second object studentB in a new local scope
as shown below,
int
main(){
Student
studentA(“Ahmad”,1);
{
Student studentB = studentA;
}
}
Now
if we will try to print the name of object studentA our program will not show
any output as name is pointing to some irrelevant memory address,
Complete
program code is given below,
#include
<iostream>
using
namespace std;
class
Student{
char * name;
int rollNo;
public:
Student(char * aName, int
arollNo){
name = new char[strlen(aName)+1];
strcpy(name,aName);
rollNo = arollNo;
}
Student(const Student &obj){
name = obj.name;
rollNo = obj.rollNo;
}
void showName(){
cout<<name<<endl;
}
~Student(){
delete []name;
}
};
int
main()
{
Student
studentA("AHMAD",1);
{
Student studentB = studentA;
/*copy constructor is implicitly
called at this point*/
}
studentA.showName();
system("pause");
return 0;
}
|
Secondly
if for some reason we change name of object studentA the value of object
studentB will also be changed as it pointing to same memory location.
#include <iostream>
using namespace std;
class Student{
char * name;
int
rollNo;
public:
Student(char
* aName, int arollNo){
name = new
char[strlen(aName)+1];
strcpy(name,aName);
rollNo
= arollNo;
}
Student(const
Student &obj){
name =
obj.name;
rollNo =
obj.rollNo;
}
void
setName(char * aName){
strcpy(name,aName);
}
void showName(){
cout<<name<<endl;
}
~Student(){
delete []name;
}
};
int main()
{
Student
studentA("AHMAD",1);
Student
studentB = studentA;
/*copy
constructor is implicitly called at this point*/
studentA.showName();
studentB.showName();
studentA.setName("MOEEN");
studentA.showName();
studentB.showName();
system("pause");
return 0;
}
|
We
resolve these two issues using deep copy.
We
write deep copy code in copy constructor so that when we create new object from
an existing object using copy constructor we also allocate new dynamic memory
for data members involving dynamic memory as shown below,
Student::Student(
const Student & obj){
int len = strlen(obj.name);
name = new char[len+1]; //
assignming new
/*dynamic memory to
data member name of char * type for newly created object.*/
strcpy(name, obj.name);
…
//copy rest of the data members in
the same way
}
Now we see what happens when we
created objects in main as shown below,
int main(){
Student
studentA(“Ahmad”,1);
{
Student studentB = studentA;
}
}
Now
when we will execute code with object studentB in local scope our code still
works fine and shows name for object studentA as now deletion of object
studentB has no effect on object studentA as shown below,
Example
#include
<iostream>
using
namespace std;
class
Student{
char * name;
int rollNo;
public:
Student(char * aName, int
arollNo){
name = new char[strlen(aName)+1];
strcpy(name,aName);
rollNo = arollNo;
}
Student(const Student &obj){
name = new char[strlen(obj.name)+1];
strcpy(name,obj.name);
rollNo = obj.rollNo;
}
void showName(){
cout<<name<<endl;
}
~Student(){
delete []name;
}
};
int
main()
{
Student
studentA("AHMAD",1);
{
Student studentB = studentA;
/*copy constructor is implicitly
called at this point*/
}
studentA.showName();
system("pause");
return 0;
}
|
- In case our
class doesn’t involve dynamic memory then default copy constructor that
performs shallow copy works fine.
- In case our
class has any data member involving dynamic memory we have to write our
own code in copy constructor to perform deep copy.
- Copy
constructor is normally used to perform deep copy
- If we do not
make a copy constructor then the compiler performs shallow copy
- Shallow copy
performs bitwise copy.
- Destructor
is used to free memory that is allocated through dynamic allocation. We
have to free memory allocated using new operator by over self in
destructor otherwise it remain occupied even after our program ends.
- Destructor is
used to perform house keeping operations.
- Destructor is
a function with the same name as that of class, but preceded with a tilde
‘~’
Example
class Student
{
…
public:
~Student(){
if(name){
delete
[]name;
}
}
}
Overloading
Destructors
cannot be overloaded.
Sequence of Calls
Constructors
and destructors are called automatically
Constructors
are called in the sequence in which object is declared
Destructors
are called in reverse order
Example
Student::Student(char * aName){
…
cout
<< aName << “Constructor\n”;
}
Student::~Student(){
cout
<< name << “Destructor\n”;
}
};
Example
int main()
{
Student
studentB(“Ali”);
Student
studentA(“Ahmad”);
return
0;
}
Example
Output:
Ali
Constructor
Ahmad
Constructor
Ahmad
Destructor
Ali
Destructor
|
In
accordance to principle of information hiding data members of a class are
declared as private so that outside world can not access the private data of
the object only an interface is provided to outside world in the form of
functions.
Accessor
functions are also used to access private data of the object, we provide
accessor functions to get and set private data members of the class.
We
also add error checking code in accessor functions to reduce errors so that
object doesn’t move in illegal state.
Example – Accessing
Data Member
Example - Setter
class Student{
…
int
rollNo;
public:
void
setRollNo(int aRollNo){
rollNo
= aRollNo;
}
};
Avoiding Error
void Student::setRollNo(int aRollNo){
if(aRollNo
< 0){
rollNo = 0;
}
else
{
rollNo = aRollNo;
}
}
Example - Getter
class Student{
…
int
rollNo;
public:
int
getRollNo(){
return
rollNo;
}
};
Good Practice:
Never
return a handle to a data member from getter function because you are never
sure that function accessing the reference will not change the value of the
variable.
Consider
the code of a general class given below,
class Student{
int
rollNo;
char
*name;
float
GPA;
public:
int
getRollNo();
void
setRollNo(int aRollNo);
…
};
The
compiler reserves space for the functions defined in the class
·
Space
for data is not allocated (since no object is yet created)
this Pointer
Student s1, s2, s3;
this Pointer
·
Function
space is common for every variable
·
Whenever
a new object is created:
o
Memory
is reserved for variables only
o
Previously
defined functions are used over and over again
Memory layout for
objects created:
this Pointer
·
Address
of each object is passed to the calling function.
·
This
address is de-referenced by the functions and hence they act on correct objects
Passing this
Pointer
·
Whenever
a function is called the this pointer is passed as a parameter to that
function.
·
Function
with n parameters is actually called with n+1 parameters
Example
void Student::setName(char *)
is internally represented as,
void Student::setName(char *, const Student *)
Declaration of this
DataType * const this;
Compiler Generated
Code
Post a Comment
Don't Forget To Join My FB Group VU Vicky
THANK YOU :)