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

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

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

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

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

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

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

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

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

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

[cpp]  view plain  copy
 print ?
  1. class String{  
  2. private:  
  3.     char *m_data;//成员变量,用于保存字符串  
  4.   
  5. public:  
  6.     String(const char *str=NULL);//普通构造函数  
  7.     String(const String &other);//拷贝构造函数  
  8.     ~String();//析构函数  
  9.     String &operator=(const String &other);//赋值函数  
  10. };  

扫描二维码关注公众号,回复: 994534 查看本文章

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

[cpp]  view plain  copy
 print ?
  1. //String的普通构造函数  
  2. String::String(const char *str)  
  3. {  
  4.     if (str==NULL)  
  5.     {  
  6.         m_data=new char[1];  
  7.         *m_data='\0';  
  8.     }  
  9.     else  
  10.     {  
  11.         int length=strlen(str);  
  12.         m_data=new char[length+1];  
  13.         strcpy(m_data,str);  
  14.     }  
  15. }  
  16. //String类的析构函数  
  17. String::~String(void)  
  18. {  
  19.     delete [] m_data;  
  20. }  

刚刚上面说,如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。以类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的拷贝构造函数与赋值函数

[cpp]  view plain  copy
 print ?
  1. // 拷贝构造函数  
  2. String::String(const String &other)  
  3. {  
  4.     // 允许操作other的私有成员m_data  
  5.     int length=strlen(other.m_data);  
  6.     m_data=new char[length+1];  
  7.     strcpy(m_data,other.m_data);  
  8. }  
  9.   
  10. //赋值函数  
  11. String & String::operator = (const String &other)  
  12. {  
  13.     //检查自赋值  
  14.     if (this==&other)  
  15.     {  
  16.         return *this;  
  17.     }  
  18.     //释放原有的内存资源  
  19.     delete []m_data;  
  20.     //分配新的内存资源,并复制内容  
  21.     int length=strlen(other.m_data);  
  22.     m_data=new char[length+1];  
  23.     strcpy(m_data,other.m_data);  
  24.     //返回本对象的引用  
  25.     return *this;  
  26. }  

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

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

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

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

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

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

猜你喜欢

转载自blog.csdn.net/dongapple/article/details/77725214