生成一个对象的副本有2种途径:
途径1:建立一个新对象,将一个已有对象数据成员的值取出来 赋给新对象。
途径2:使用复制构造函数。
复制构造函数是一种特殊的构造函数,用来生成一个对象的副本。
复制构造函数的作用:
使用一个已经存在的对象初始化一个同类的新对象。
复制构造函数的特点:
复制构造函数名与类名相同,并且也没有返回值类型(被系统自动调用)。
复制构造函数可写在类中,也可以写在类外。
复制构造函数要求有一个类类型的引用参数
如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。

复制构造函数的形参为本类的对象引用
class 类名
{
public : 类名(形参);//构造函数
类名([const ]类名 &引用名<形式参数名>);//复制构造函数
//[const]保护实参对象只读,不能被更改,可以不写

};
类名::类名([const] 类名 &引用名) //复制构造函数的实现
{ 函数体 }

举例
class Point 
{ 
	public: 
		Point(int xx=0,int yy=0) {X=xx; Y=yy;} //带默认形参值的构造函数 
		Point(Point&  p); //复制构造函数 参数是同类的引用
		int GetX()   {return X;} 
		int GetY()   {return Y;} 
	private: 
		int  X,Y; 
};
Point::Point (Point& p) //复制构造函数的实现 
{ 
	X=p.X;   //把形参的值取出来赋给类的数据成员
	Y=p.Y; 
	cout<<"复制构造函数被调用"<<endl; 
}

在以下情况调用复制构造函数:
1、当用类的一个对象去初始化该类的另一个对象时,系统自动调用复制构造函数实现拷贝赋值。

#include <iostream> 
using namespace std; 
class Complex   //复数类
{ 
	public: 
		Complex(double r, double i) {  real = r;  imag = i; }  //带参数的构造函数
		Complex( Complex & c) 
		{ 
			real = c.real; 
			imag = c.imag; 
			cout<<"copy constructor!"<<endl; 
		} 
	private: 
		double real, imag;   //实部虚部
}; 
int main()
{
Complex c1(1,2);     //定义c1类的对象  调用构造函数
Complex c2(c1);      ///调用复制构造函数  没有调用构造函数时的传递,在复制构造函数中c为c1的别名
Complex c3= c1; //用对象c1去初始化该类 的另一个对象c2|c3时复制构造函数被调用
c3=c2; //非初始化,不调用复制构造函数(只有在新定义时调用构造函数) 
return 0; 
}

程序执行结果为:
copy constructor!
copy constructor!

2、若函数的形参为类对象,调用函数时,实参传递给形参,系统自动调用复制构造函数,用实参对象去初始化形参对象。
如果某函数有一个参数是类Complex的对象,那么该函数被调用时,类Complex的复制构造函数将被调用。

void func(Complex c) { };  //c是临时的,只有调用的时候申请存储空间
int main( ) 
{ 
	Complex c1(1,2); 
	func(c1);  //调用复制构造函数  实际参数c1传递给形式参数c  在复制构造函数中形参是实参的别名,参与引用的其实是实际参数
	return 0; 
}

程序执行结果为:
copy constructor!

3、如果函数的返回值是类的对象,函数执行完返回调用者时 ,需要生成一个临时对象作为函数返回结果,此时,系统需要调用复制构造函数完成临时对象的生成。

Complex func() 
{ 
	Complex c1(1,2); 
	return c1;  //调用复制构造函数 
};

除此之外,还将调用析构函数销毁临时对象。
如果有形如 a=func()的表达式,还将调用赋值函数完成赋值操作.

默认的复制构造函数
如果没有为类声明拷贝初始化构造函数,则编译器自己 生成一个默认的复制构造函数。
这个构造函数执行的功能:
用作为初始值的对象的 每个数据成员的值, 初始化将要建立的对 象的对应数据成员

#include <iostream> 
using namespace std; 
class CMyclass 
{ 
	int a; 
	public: 
		CMyclass(int aa =0) {a=aa;} 
		void disp( )
		{cout<<a<<endl;} 
}; 
int main( ) 
{ 
	CMyclass c1(5); 
	CMyclass c2(c1);    //c1和c2的值一样
	c1.disp(); 
	c2.disp(); 
	return 0; 
}

浅复制与深复制
浅复制:
被复制对象的所有变量都含有与原来的对象相同的值,而其所有的对其他对象的引用都仍然指向原来的对象。
说明:
一个对象中的数据成员:
有的是值类型的数据,它的值就是简单的值,
有的是引用类型的数据,它的值是地址,例如:指针类型的成员函数。
浅复制在复制时,将这个对象的值数据和引用数据(均为非静态数据)全部复制过去,获得了这个对象的值和地址。 即:当其中一个对象的引用字段所指向的地址中的变量变化时,所有浅复制对象中 的该引用字段都会发生变化。
默认复制构造函数所进行的是简单数据复制,即浅复制
浅复制的例子

#include <iostream> 
using namespace std; 
class Test 
{ 
	private: 
		int a,b; 
	public: 
		Test(int x, int y)     //提供的形式参数,是为了给数据成员直接初始化的 
		{  
			a=x;        
			b=y;    
		} 
		Test(const Test& C)   //复制构造函数,提供一个同类型对象作为参数 
		{  
			a=C.a;    
			b=C.b;    
		} 
		void show () 
		{     cout<<a<<"  "<<b<<endl;   } 
}; 
int main() 
{ 
	Test xx(100,10);    //执行构造函数Test::Test(int x, int y) 
	Test yy(xx);      //执行构造函数Test::Test(const Test& C) 
	Test zz=xx;      //也执行构造函数Test::Test(const Test& C) 
	yy.show(); 
	zz.show(); 
	return 0; 
}

浅复制存在问题

#include <iostream> 
#include <cstring> 
using namespace std; 
class Test 
{ 
	private: 
		int a; 
		char *str;   //指针类型
	public: 
		Test(int b, char *s)  //构造函数
		{   
			a=b;        
			str=new char[strlen(s)+1];    //申请空间存储s的长度加一(结束字符)    
			strcpy(str,s);     
		} 
		void setA(int b)    
			{a=b; } 
		void setStr(char *s)    
			{ strcpy(str,s);   } 
		void show ()    
			{cout<<a<<","<<str<<endl;   } 
};
int main() 
{ 
Test xx(100,"hello"); 
Test yy(xx);   //xx,yy指向相同的地址
xx.show();     yy.show(); 
xx.setA(80);  xx.setStr("abc"); 
xx.show();     yy.show(); 
return 0; 
}

运行结果
100,hello
100,hello
80,abc
100,abc
当类的数据成员含有指针类型时,浅复制构造函数存在问题。此时需要定义深复制构造函数。
深复制:通过一个对象初始化另一个对象时,不仅将被复制对象中所有非引用类型的字段复制给新对象, 也将引用类型所指向地址中存储的对象复制给新的对象。
定义支持深复制的复制构造函数

  1. 缺省的复制构造函数是浅复制构造函数
  2. 深复制构造函数必须显式定义
  3. 当成员变量中含有指针变量时,需要定义深复制构造函数
  4. 深复制构造函数的特点
    ① 定义:类名::类名([const] 类名 &对象名);
    ② 成员变量的处理:对指针类型的成员变量,使用new操作符进行空间的申请,然后进行相关的复制操作

修改

public: 
	Test(int b, char *s) 
	{   
		a=b;        
		str=new char[strlen(s)+1];        
		strcpy(str,s);     
	} 
	void setA(int b)    
		{a=b; } 
	void setStr(char *s)    
		{ strcpy(str,s);   } 
	void show ()    
		{cout<<a<<","<<str<<endl;   } 
	Test(const Test& C)    //深复制
	{ 
		a=C.a; 
		str=new char[strlen(C.str)+1];   //申请了独立的存储空间
		strcpy(str,C.str);    //str=C.str; 
	} 
};

运行结果
100,hello
100,hello
80,abc
100, hello