赞
踩
2020-07-13
拷贝构造函数是一种特殊的构造函数,在创建对象时,它是使用同一类中之前创建过的对象来初始化新创建的对象。如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型成员变量,调用其相应类型的拷贝构造函数。
我们在编写程序的过程中,如果不主动编写拷贝构造函数和赋值函数,编译器将会调用默认的函数,如果类中含有指针变量,那么如果使用的默认的函数就会有错误,下面首先我们先进行简单的介绍,之后再用具体的例子来加以说明。
1.拷贝构造函数和赋值函数
拷贝构造函数,顾名思义,它是一个构造函数,所以它是在对象创建的时候被主动调用的函数,可以将
另外一个对象的变量拷贝给当前对象。赋值函数,是在对象已经存在的情况下才会进行调用。
#include <iostream> #include <set> using namespace std; class Test { public: Test() { // 默认构造函数 cout << "Test()" << endl; }; Test(int v) :value(v) { // 带参数的构造函数 cout << "Test(int v)" << endl; } Test(const Test& obj){ //拷贝构造函数 cout << "Test(const Test& obj)" << endl; } Test& operator=(const Test& obj) { cout << "Test& operator=(const Test& obj)" << endl; return *this; } ~Test() { // 析构函数 } private: int value; }; int main() { Test a(1); Test c = a; Test d; d = a; getchar(); return 0; }
(1)拷贝构造函数和赋值函数
Test c = a;
d = a;
这里第一个"=“时对象c还没有存在,所以这里调用的是拷贝构造函数,第二个”="时对象d已经存在了,所以调用的是赋值函数
(2)拷贝构造函数
拷贝构造函数是其它构造函数的重载函数,它的参数是const对象的引用,const比较容易理解,我们将一个对象拷贝给另外一个对象,那该对象的值我们是不希望被改变了的,另外一个原因是,添加 const 限制后,就可以将 const 对象和非 const 对象传递给形参了,因为非 const 类型可以转换为 const 类型,如果没有 const 限制,就不能将 const 对象传递给形参,因为 const 类型不能转换为非 const 类型,这就意味着,不能使用 const 对象来初始化当前对象了;那么这里我们为什么选择传递对象的引用作为函数的参数呢?一个原因是引用传参的时候,形参是实参的一个别名,也就是说它们俩其实是相同的,但是如果我们不传递引用的话,在进入函数的时候,会另外分配一块存储空间给形参使用,形参将会初始化实参的值,在函数调用结束的时候,这块存储空间将会被释放掉,如果说对象比较大的话,在这个初始化的过程中将会比较的耗时;还有一个十分重要的原因是我们在调用拷贝构造函数的时候,如果传递的是对象的话,由于要将实参的值赋给形参,将会调用拷贝构造函数进行赋值,那么就会形成一个死循环,如果您将上述拷贝构造函数中的"&"删除掉,程序将会报错。
(3)赋值函数
赋值函数用到的是运算符的重载,它的返回值是对象的引用,参数是const对象的引用。对于返回值而言,我们希望保留运算符原有的特性,考虑到a=b=c,这个赋值语句的顺序应该是a=(b=c),所以赋值函数重载函数的返回值应该是类的对象,返回对象的引用是因为如果返回对象的话,将会把原先对象的值赋值给临时的对象,这样还会调用一次拷贝构造函数。赋值函数的参数是对象的引用,一个原因是节省时间,另外一个原因是使函数可以传递const的对象。
#include <iostream> using namespace std; class Test { public: Test() { // 默认构造函数 cout << "Test()" << endl; }; Test(int v) :value(v) { // 带参数的构造函数 cout << "Test(int v)" << endl; } Test(const Test& obj){ //拷贝构造函数 cout << "Test(const Test& obj)" << endl; } Test operator=(const Test& obj) { cout << "Test& operator=(const Test& obj)" << endl; return *this; } ~Test() { // 析构函数 } private: int value; }; int main() { Test a(1); Test c = a; Test d; d = c = a; getchar(); return 0; }
由于赋值函数的返回值是对象,这里在函数返回时就调用了拷贝构造函数。
2.何时调用拷贝构造函数
(1)定义一个对象时,以本类另一个对象作为初始值,发生拷贝构造。
(2)如果函数的形参是类的对象,调用函数时,将使用实参初始化形参,发生拷贝构造。
(3)如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化
一个临时的无名对象,传递给主调函数,发生拷贝构造。
3.浅拷贝和深拷贝
浅拷贝:如果用默认的拷贝构造函数(赋值函数)去赋值有指针类型的成员变量的对象,将会使两个对象
的指针地址也是一样的,也就是说这两个对象的指针成员变量指向的是相同的地址。
深拷贝:每个对象拥有自己的资源,此时需要显示提供拷贝构造函数和赋值函数。
如果调用了默认的拷贝构造函数(赋值函数),在拷贝过程中是按字节复制的,对于指针型成员变量只复制指针本身,而不复制指针所指向的目标,这将会使同一指针指向相同的区域,同时也会导致同一块资源被释放多次,从而造成错误。
下面的例子是显示定义的赋值函数。
#include <iostream> #include <string.h> using namespace std; class Test { public: Test():ptr(new char[1]) { // 带参数的构造函数 cout << "Test():ptr(new char[1])" << endl; } Test operator=(const Test& obj) { if (this == &obj) { // s=s return *this; } delete[] ptr; ptr = new char[strlen(obj.ptr) + 1]; strcpy(ptr, obj.ptr); cout << "Test& operator=(const Test& obj)" << endl; return *this; } Test& operator=(const char *s) { delete[] ptr; ptr = new char[strlen(s) + 1]; strcpy(ptr, s); cout << "Test& operator=(const Test& obj)" << endl; return *this; } ~Test() { // 析构函数 delete[] ptr; } private: char* ptr; }; int main() { Test a; a= "test"; Test b; b= a; Test d; getchar(); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。