C++ String类的构造函数、拷贝构造函数的实现

构造函数、析构函数与赋值函数是每个类最基本的函数,在一些公司的面试中也会经常问到这方面的问题。每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不手动编写上述函数,C++编译器将自动为类A生成四个缺省的函数

   A(void);                    // 缺省的无参数构造函数

   A(const A &a);                // 缺省的拷贝构造函数

   ~A(void);                    // 缺省的析构函数

    A& operate =(const A &a);    // 缺省的赋值函数

虽然有自动生成,但是还是有必要手动写上述函数的。因为:

(1)如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人Stroustrup的好心好意白费了。

(2)“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。

下面以类String的设计与实现为例,深入探讨这个道理。String的结构如下:

class String{
private:
	char *m_data;//成员变量,用于保存字符串

public:
	String(const char *str=NULL);//普通构造函数
	String(const String &other);//拷贝构造函数
	~String();//析构函数
	String &operator=(const String &other);//赋值函数
};

String类的普通构造函数和析构函数实现如下:

//String的普通构造函数
String::String(const char *str)
{
	if (str==NULL)
	{
		m_data=new char[1];
		*m_data='\0';
	}
	else
	{
		int length=strlen(str);
		m_data=new char[length+1];
		strcpy(m_data,str);
	}
}
//String类的析构函数
String::~String(void)
{
	delete [] m_data;
}

刚刚上面说,如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。以类String的两个对象a,b为例,假设a.m_data的内容为“hello”,b.m_data的内容为“world”。位拷贝拷贝的是地址,而值拷贝则拷贝的是内容。现将a赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data,虽然b.m_data所指向的内容会变成”hello”,但是这将造成三个错误:一是b.m_data原有的内存没被释放,造成内存泄露;二是b.m_data和a.m_data指向同一块内存,a或b任何一方变动都会影响另一方;三是在对象被析构时,m_data被释放了两次。

对于编译器,如果不主动编写拷贝函数和赋值函数,它会以“位拷贝”的方式自动生成缺省的函数。如果重写赋值函数和拷贝构造函数后,b.m_data=a.m_data,进行的是值拷贝,会将a.m_data的内容赋给b.m_data,b.m_data还是指向原来的内存区域,但是其内容改变。

有下面4个语句:

String a(“hello”);

String b(“world”);

String c = a;    // 调用了拷贝构造函数,最好写成 c(a);

c = b; // 调用了赋值函数

第3语句的风格较差,宜改写成String c(a) 以区别于第4语句

 

下面是类String的拷贝构造函数与赋值函数

// 拷贝构造函数
String::String(const String &other)
{
	// 允许操作other的私有成员m_data
	int length=strlen(other.m_data);
	m_data=new char[length+1];
	strcpy(m_data,other.m_data);
}

//赋值函数
String & String::operator = (const String &other)
{
	//检查自赋值
	if (this==&other)
	{
		return *this;
	}
	//释放原有的内存资源
	delete []m_data;
	//分配新的内存资源,并复制内容
	int length=strlen(other.m_data);
	m_data=new char[length+1];
	strcpy(m_data,other.m_data);
	//返回本对象的引用
	return *this;
}

类String拷贝构造函数与普通构造函数(参见9.4节)的区别是:在函数入口处无需与NULL进行比较,这是因为“引用”不可能是NULL,而“指针”可以为NULL。

类String的赋值函数比构造函数复杂得多,分四步实现:

(1)      第一步,检查自赋值。

(2)      第二步,用delete释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。

(3)      第三步,分配新的内存资源,并复制字符串。

(4)      第四步,返回本对象的引用,目的是为了实现象 a = b = c 这样的链式表达。注意不要将 return *this 错写成 return this.


Ref

高质量C++/C编程指南

http://man.chinaunix.net/develop/c&c++/c/c.htm#_Toc520634051



  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值