拷贝构造函数
拷贝构造函数一般为:
类名(const 类名& C);它必须的一个参数是本类型的一个引用变量
默认的拷贝构造函数是浅拷贝。
拷贝构造函数常出现的地方:
a对象以值传递的方式传入函数参数
//全局函数,传入的是对象
voidg_Fun(CExample C)
{
cout<<"test"<<endl;
}
intmain()
{
CExample test(1);
g_Fun(test);
//当调用g_Fun()时,会产生以下几个重要步骤:创建一个临时的对象,调用拷贝构造函数将test的值拷贝到这个临时对象中,等g_Fun()执行完后, 析构掉这个临时对象,这也是为什么我们传对象的时候传数值比传指针(或者是引用对象)慢的原因。
return 0;
}
测试:
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a = b;
cout<< "creat:" << a<< endl;
}
//拷贝构造
CExample(const CExample&C)
{
a = C.a;
cout<< "copy" << endl;
}
//析构函数
~CExample()
{
cout<< "delete:" << a<< endl;
}
void Show()
{
cout<< a << endl;
}
};
//全局函数,传入的是对象
void g_Fun(CExample C)
{
cout<< "test" << endl;
}
void fun1()
{
CExample test(1);
//传入对象,也就是传数值
g_Fun(test);
///当调用g_Fun()时,会产生以下几个重要步骤:创建一个临时的对象,调用拷贝构造函数将test的值拷贝到这个临时对象中,等g_Fun()执行完后, 析构掉这个临时对象,这也是为什么我们传对象的时候传数值比传指针(或者是引用对象)慢的原因。
}
运行结果:
B、对象以值传递的方式从函数返回
CExample1 g_Fun2()
{
cout << "g_Fun2函数:" << endl;
CExample1 temp(111);
return temp;
//当这个函数执行到return时,先会创建一个临时变量,再调用拷贝构造函数把temp的值拷贝到这个临时变量中,等这个函数执行到最后,析构temp局部变量,再将这个临时变量作为返回对象返回
}
void text2()
{
CExample1 pC = g_Fun2();
cout << "主函数:" << endl;
pC.Show();
}
结果:
C、对象需要通过另外一个对象进行初始化
//(3)对象需要通过另外一个对象进行初始化
void text3()
{
CExample1 A(100);
CExample1 B = A;//在这里相当于CExample B(A);是调用了拷贝构造函数,如果类中没有定义自己的拷贝构造函数,系统会调用系统自带的,但是自带的拷贝构造函数只是将对象的数值拷贝过去,如果是类中有指针对象时,此时容易出现错误,即A,B两个类对象,对应的指针变量,由于拷贝构造函数只将A的指针只拷贝数值给B对应的指针,这样两个指针指向同一块内存上,释放的时候同一块内存释放2次出现错误,要解决这个问题就要用到浅拷贝(只拷贝数值)和深拷贝(即拷贝数值,也同时针对指针创建内存),主要区别:针对有指针的类,指针指向的内存,他是否会为其再申请空间,回再申请空间的就是深拷贝,否则就是浅
}
浅拷贝
所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。针对有指针的类,指针指向的内存,他不会为其再申请空间,只是简单的把两个指针的值拷贝下,即两个指针指向同一块内存,易出现同一块内存上,释放的时候同一块内存释放2次出现错误。
深拷贝主要是针对有指针的类,指针指向的内存,他会为其再申请空间,再把数值拷贝进去,为新对象重新动态分配空间
//深拷贝
class Rect
{
public:
Rect() // 构造函数,p指向堆中分配的一空间
{
p = new int(100);
}
Rect(const Rect& r)
{
width = r.width;
height = r.height;
p = new int; //区别深拷贝与浅拷贝关键:深拷贝主要是针对有指针的类,指针指向的内存,他会为其再申请空间,再把数值拷贝进去,为新对象重新动态分配空间
*p = *(r.p);
}
~Rect() // 析构函数,释放动态分配的空间
{
if (p != NULL)
{
delete p;
}
}
private:
int width;
int height;
int *p; // 一指针成员
};
void test4()
{
Rect p1;
Rect p2 = p1;
}
防止默认拷贝发生
将拷贝构造函数设置为私有的
// 防止按值传递
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a = b;
cout << "creat: " << a << endl;
}
private:
//拷贝构造,只是声明
CExample(const CExample& C);
public:
~CExample()
{
cout << "delete: " << a << endl;
}
void Show()
{
cout << a << endl;
}
};
//全局函数
void g_Fun(CExample C)
{
cout << "test" << endl;
}
int main()
{
CExample test(1);
//g_Fun(test); 按值传递将出错
return 0;
}
拷贝构造函数和赋值符的区别
CTest a;
CTest b(a);
CTest c = a; //注意,这里仍然是使用复制构造函数
a = c; //使用赋值符
1从概念上区分:
复制构造函数是构造函数,而赋值操作符属于操作符重载范畴,它通常是类的成员函数
复制构造函数原型ClassType(const ClassType &);无返回值
赋值操作符原型ClassType& operator=(const ClassType &);返回值为ClassType的引用,便于连续赋值操作
3.从使用的场合来区分:
复制构造函数用于产生对象,它用于以下几个地方:函数参数为类的值类型时、函数返回值为类类型时以及初始化语句,而赋值操作符要求‘=’的左右对象均已存在,它的作用就是把‘=’右边的对象的值赋给左边的对象
4.当类中含有指针成员时,两者的意义有很大区别
复制构造函数需为指针变量分配内存空间,并将实参的值拷贝到其中;而赋值操作符它实现的功能仅仅是将‘=’号右边的值拷贝至左值,在左边对象内存不足时,先释放然后再申请。当然赋值操作符必须检测是否是自身赋值,若是则直接返回当前对象的引用而不进行赋值操作