C++学习笔记20,复制构造函数


C++有一种特殊的构造函数叫做复制构造函数,允许所创建的对象是另一个对象的精确副本。如果没有编写,编译器将自动生成一个。利用源对象相应数据成员的值初始化新对象的每个数据成员。

#include <iostream>
using namespace std;
class A
{
private:
	int i;
	string str;
public:
	A(int ii,string s):i(ii),str(s){
	
	}	
	/*
	A(const A &a){//为了提高效率,一般参数使用引用,并且使用const保证不改变原来的值
		cout<<"before copy ,i="<<i<<",str="<<str<<endl;
		//这一句间接说明了int类型如果不赋初始值时其值时不确定的,取决于当时内存中的值
		i=a.i;
		str=a.str;
		cout<<"复制函数已经调用"<<endl;
	}
	*/
	A(const A &a):i(a.i),str(a.str){
		//最好使用初始化器的方式初始化
		cout<<"复制函数已经调用"<<endl;
	}
	void show()const{
		cout<<"i="<<i<<",str="<<str<<endl;
		cout<<"address:&i="<<&i<<",&str="<<&str<<endl<<endl;
	}
};
int main()
{
	A a(10,"hello");
	cout<<"this is a:"<<endl;
	a.show();
	
	cout<<"this is b:"<<endl;
	A b(a);	
	b.show();
   
	cout<<"this is c:"<<endl;
	A c=b;//注意,这一个调用的也是复制构造函数,而不是赋值构造函数
	//类似于声明的语句会调用复制构造函数,原因时A c=b;c当时还没创建,是不会调用operator=方法的。
	c.show();
}

运行结果:


 

但有一点要注意的就是,如果构造函数体内有数据成员是使用new来分配空间的话,如果不手动编写复制构造函数,就会出错!

 

#include <iostream>
#include <cstring>
using namespace std;
class A
{
private:
	char *str;
public:
	A(const char *s){
		int len=sizeof(s);
//因为是赋值构造函数,所以在this对象中还没有str,因此不用delete []str;
		str=new char[len+1];
		strcpy(str,s);
	}
	//不定义复制构造函数
	//让编译器自动生成一个
	~A(){
		cout<<"start delete!"<<endl;
		delete []str;
		cout<<"delete complete!"<<endl;
	}
	void show()const{
		cout<<"str="<<str<<endl;
		cout<<"address:&str="<<&str<<endl<<endl;
	}
};
int main()
{
	{
	A a("hello");
	cout<<"this is a:"<<endl;
	a.show();
	
	cout<<"this is b:"<<endl;
	A b(a);	
	b.show();
   
	cout<<"this is c:"<<endl;
	A c=b;//注意,这一个调用的也是复制构造函数,而不是赋值构造函数
	//类似于声明的语句会调用复制构造函数,原因时A c=b;c当时还没创建,是不会调用operator=方法的。
	c.show();
	}
}

运行结果:

看似似乎a,b,c的地址都不一样,一开始我也很疑惑。但是在gdb下面调试发现其实并不是这样。

而且运行的时候也提示了两次free了同一内存。看析构中的输出也可以分析出是在free中出现了问题。

用GDB调试查看:

可以看出,实际上str地址都是一样的。都是指向同一个内存单元0X603010,而&a.str的地址都不相同。难道是说&a.str是指存放这个变量地址的地址?似乎只有这样才能说的通。使用p a.str查看,果然如此。


C++11同样提供了显式默认或者删除复制构造函数。方法和默认构造函数一致。

显式默认复制构造函数。

A(const A &a)=default;

显式删除复制构造函数

A(const A &a)=delete;

这样,编译器就将禁止使用复制构造函数。编译结果:

还有一种方法就是利用移动语义以及右值引用,这里由于我也不是和熟练,就先不写了。

 

 

 


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注