C++笔记(八)构造函数与析构函数

代码中的MyString类包含一个指针成员,他指向动态分配的内存。复制这个类的对象时,将复制其指针成员,但不会复制指针指向的缓冲区,其结果会造成两个对象指向同一块内存区,这称为浅复制,会威胁程序的稳定性。

#include<iostream>
using namespace std;

class MyString{
private:
	char *Buffer;
public:
	MyString(const char *ini){
		if(ini!=NULL){
			Buffer=new char[strlen(ini)+1];
			strcpy(Buffer,ini);
		}
		else{
			Buffer=NULL;
		}
	}

	~MyString(){
		cout<<"析构函数调用"<<endl;
		if(Buffer!=NULL){
			delete[] Buffer;
		}

	}

	int GetLnegth(){
		return strlen(Buffer);
	}

	const char*GetString(){
		return Buffer;
	}
};

void UseString(MyString s){
	cout<<s.GetLnegth()<<endl;
	cout<<s.GetString()<<endl;
	return;
}
int main(){
	MyString s1("this is string one");
	UseString(s1);

	return 0;
}

函数崩溃出错,与上一篇博文最后一段代码相比,只是将输出代码写进了void UseString(String s)函数中。运行时,主函数中Mystring的对象s1被复制到函数UseString(String s)的形参中并被使用,而函数void UseString(String s)被声明为按值传参,因此s1的指针成员的值被复制到s中,就是说s1.Buffers.Buffer指向同一内存单元。
函数void UseString(String s)返回时,变量s不再在作用域内,因此被销毁–调用Mystring的析构函数(代码18-24行);delete[] Bufffer释放内存。这将导致主函数中的s1.Buffer指向的内存无效,而等到main()函数执行完后,s1不再在作用域内而被销毁时(实际上该内存已被销毁,不能再次被销毁)出错。

一、使用复制构造函数确保深复制

复制构造函数在对象被复制(包括对象按值传递给函数)时被编译器调用。
申明语法如下:

#include<iostream>
using namespace std;

class MyString{
private:
	char *Buffer;
public:
	MyString(const char *ini){
		if(ini!=NULL){
			Buffer=new char[strlen(ini)+1];
			strcpy(Buffer,ini);
			cout<<"Buffer points to :0x"<<hex;
			cout<<(unsigned int*)Buffer<<endl;
		}
		else{
			Buffer=NULL;
		}
	}
	MyString(const MyString& CopySource){
		cout<<"复制构造函数调用"<<endl;
		if(CopySource.Buffer!=NULL){
			Buffer=new char[strlen(CopySource.Buffer)+1];
			strcpy(Buffer,CopySource.Buffer);
			cout<<"Buffer points to :0x"<<hex;
			cout<<(unsigned int*)Buffer<<endl;

		}
		else{
			Buffer=NULL;
		}
	}
	~MyString(){
		cout<<"析构函数调用"<<endl;
		if(Buffer!=NULL){
			delete[] Buffer;
		}

	}

	int GetLnegth(){
		return strlen(Buffer);
	}

	const char*GetString(){
		return Buffer;
	}
};

void UseString(MyString s){
	cout<<s.GetLnegth()<<endl;
	cout<<s.GetString()<<endl;
	return;
}
int main(){
	MyString s1("this is string one");
	UseString(s1);

	return 0;
}

输出:
Buffer points to :0x013D9E20
复制构造函数调用
Buffer points to :0x013D9520
12
this is string one
析构函数调用
析构函数调用
请按任意键继续. . .

两个buffer的地址分别是0x013D9E200x013D9520,确认是深复制。

  1. 类包含原始指针成员时,务必编写复制构造函数和复制赋值运算符;
  2. 编写复制构造函数时,务必接受源对象的参数申明为const引用;

二、移动构造函数

在需要多次调用复制构造函数来传值时,编译器严格调用复制构造函数反而降低了性能,为了避免这种性能瓶颈,可使用移动构造函数,语法如下:

MyString (MyString && MoveSource){
 if(MoveSource.Buffer!=NULL){
 BUffer=MoveSource.BUffer;
 MoveSource.BUffer=NULL;
}
}

三、几种特殊用途

3.1不允许复制对象的类

通过私有的复制构造函数,确保类对象不能作为参数被复制,只需将函数声明为私有的,无需实现。

class Present{
private:
Present(const Present&);
Present & operator=(const Present&);
}

3.2只能有一个实例的单例类

私有构造函数,私有赋值运算符,静态实例成员

#include<iostream>
# include<string>
using namespace std;

class Present{
private:
	Present(){};
	Present(const Present&);
	const Present&operator=(const Present&);
	string name;
public:
	static Present&GetInstantance(){
		static Present OnlyOne;
		return OnlyOne;
	}
	string getName(){
		return name;
	}
	void SteName(string s){
		name=s;
	}
};
int main(){
	Present & OnlyOne=Present::GetInstantance();
	OnlyOne.SteName("baby");
	cout<<"the name of the only present is : "<<Present::GetInstantance().getName();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值