复制构造函数

复制构造函数

提出背景

C++中的newdelete关键字给了用户更大的自由对内存进行管理,但是如果使用不当就很容易造成内存泄漏。在创建和使用类的时候,需要额外小心,因为对类的创建和销毁行为的管理不当是造成内存泄漏的重要原因。下面是一个反面教材:

class BadString{
	private:
		static int num;	//用于记录当前创建的实例化个数,这个值在每个构造函数中都会被+1
		char * str;
		int len;
	public:
		BadString();
		BadString(const char *);
		~BadString();
		void show();
		void showNum();
};

BadString::BadString(){
	cout << "Using default constructor:" << endl << "C++" << endl;
	str = new char[4];
	len = 3;
	num++;
	strcpy(str, "C++");
}

BadString::BadString(const char * s){
	cout << "Initializing String:" << endl
			<< s << endl;
	len = strlen(s);
	str = new char[len + 1];
	num++;
	strcpy(str, s);
}

BadString::~BadString(){
	cout << "Destroying String:" << endl
			<< str << endl;
	num--;
	delete [] str;
}

void BadString::show(){
	cout << str << endl;
}

void BadString::showNum(){
	cout << "Current number of strings is: " << num << endl;
}

int BadString::num = 0;	//必须要在主函数之外初始化

BadString s;
s.showNum();		//输出字符串的个数为1
BadString tem = s;	//初始化,出错的地方
tem.showNum();		//输出字符串的个数还是为1

上述程序可能可以通过编译,但是在运行的时候会报错,报错的原因是重复释放已经释放的空间,其出错的原因就是在BadString tem1 = s1;这一行代码。并且这行代码还会导致另一个错误,明明程序创建了stem两个对象,但是类中静态变量num记录到的对象个数还是1。

一个错误是num值不一致的问题。这个错误出现是因为初始化tem的时候,没有调用事先定义好的构造函数,而是调用了复制构造函数,而默认的复制构造函数中没有对num加一。复制构造函数是每当程序产生对象副本时调用的构造函数,其函数签名如下所示:

ClassName(const ClassName &);

所以上面BadString tem = s的执行效果实际上是这样:

BadString tem = BadString(s);

另一个错误是对堆内存重复释放的问题。这行代码为tem1在栈中创建了一个内存空间,并且将对象s中的每一个成员的值复制给tem。这个复制是浅复制,只是单纯复制成员的变量,而s.str是一个指针,指向的是堆内存中的一块内存,这就导致了s.strtem.str同时指向一块内存。当程序执行完毕,系统摧毁两个变量的时候就会对同一块内存释放两次,从而造成运行错误。要想解决这个问题,只需要在自己实现的复制构造函数中重新分配内存,并把s.str的值复制进去即可,记得在类声明中加入复制构造函数的声明

BadString::BadString(const BadString & s){
	num++;
	cout << "Using copy constructor for: " << s.str << endl;
	len = strlen(s.str);
	str = new char[len + 1];
	strcpy(str, s.str);
}

加入这样的复制构造函数的定义之后,上述的两个错误都将解决。

新的问题

上述的复制构造函数只是在对象初始化的时候才会调用,而对于赋值操作,编译器不会调用复制构造函数。例如:

BadString s;
s.showNum();
BadString tem;
tem = s;			//赋值操作,不会调用复制构造函数
tem.showNum();

默认的赋值运算符=的操作跟默认的复制构造函数一样,只是浅复制,所以要对=重载来实现深复制,来解决这个问题,重载=的函数形式为:

ClassName & operator=(const ClassName &);

在使用的时候(例如tem = s),=左边是调用对象,该函数的调用对象是左边的对象,所以函数直接可以访问对象中的私有成员,以修改其值。返回引用的原因是实现连续赋值。

下面是一个对BadString类重载=的例子:

BadString & BadString::operator=(const BadString & s){
	if(&s == this) return *this;
	delete [] str;	//释放原来对象中str中的空间
	len = s.len;
	str = new char[len + 1];
	strcpy(str, s.str);
	return * this;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值