|
|
|
|
Shallow Copy: |
|
The data members of one object are copied into
the data members of another object without taking any dynamic memory
pointed to by those data members into consideration. (“memberwise copy”) |
|
Deep Copy: |
|
Any dynamic memory pointed to by the data
members is duplicated and the contents of that memory is copied (via copy
constructors and assignment operators -- when overloaded) |
|
|
|
|
|
|
In every class, the compiler automatically
supplies both a copy constructor and an assignment operator if we don't
explicitly provide them. |
|
Both of these member functions perform copy
operations by performing a memberwise copy from one object to another. |
|
In situations where pointers are not members of
a class, memberwise copy is an adequate operation for copying objects. |
|
However, it is not adequate when data members
point to memory dynamically allocated within the class. |
|
|
|
|
|
Problems occur with shallow copying when we: |
|
initialize an object with the value of another
object: name s1; name
s2(s1); |
|
pass an object by value to a function or when we
return by value: |
|
name
function_proto (name) |
|
assign one object to another: |
|
s1 = s2; |
|
|
|
|
|
If name had a dynamically allocated array of
characters (i.e., one of the data members is a pointer to a char), |
|
the following shallow copy is disastrous! |
|
|
|
|
To resolve the pass by value and the
initialization issues, we must write a copy constructor whenever dynamic
member is allocated on an object-by-object basis. |
|
They have the form: |
|
|
|
class_name(const class_name &class_object); |
|
|
|
Notice the name of the “function” is the same
name as the class, and has no return type |
|
The argument’s data type is that of the class,
passed as a constant reference (think about what would happen if this was
passed by value?!) |
|
|
|
|
//name.h interface |
|
class name { |
|
public: |
|
name(char* = "");
//default constructor |
|
name(const
name &); //copy constructor |
|
~name();
//destructor |
|
name
&operator=(name &); //assignment op |
|
private: |
|
char*
ptr; //pointer to name |
|
int
length; //length of name including nul char |
|
}; |
|
|
|
#include "name.h" //name.c implementation |
|
name::name(char* name_ptr) { //constructor |
|
length
= strlen(name_ptr); //get name
length |
|
ptr =
new char[length+1]; //dynamically
allocate |
|
strcpy(ptr, name_ptr);
//copy name into new space |
|
} |
|
name::name(const name &obj) { //copy constructor |
|
length
= obj.length; //get length |
|
ptr =
new char[length+1]; //dynamically
allocate |
|
strcpy(ptr, obj.ptr);
//copy name into new space |
|
} |
|
|
|
|
|
|
|
|
|
|
Now, when we use the following constructors for
initialization, the two objects no longer share memory but have their own
allocated |
|
|
|
|
Copy constructors are also used whenever passing
an object of a class by value: (get_name returns a ptr to a char for the
current object) |
|
|
|
int main() { |
|
name
smith("Sue Smith"); //constructor with arg used |
|
|
|
//call
function by value & display from object returned |
|
cout
<<function(smith).get_name() <<endl; |
|
return
(0); |
|
} |
|
|
|
name function(name obj) { |
|
cout
<<obj.get_name() <<endl; |
|
return
(obj); |
|
} |
|
|
|
|
|
|
Using a copy constructor avoids objects
“sharing” memory -- but causes this behavior |
|
|
|
This should convince us to avoid pass by value
whenever possible -- when passing or returning objects of a class! |
|
|
|
|
Using the reference operator instead, we change
the function to be: (the function call remains the same) |
|
|
|
name &function(name &obj) { |
|
cout
<<obj.get_name() <<endl; |
|
return
(obj); |
|
} |
|
|
|