C++回顾之深浅拷贝、禁止拷贝、空类的默认成员

来源:互联网 发布:电脑编程语言 编辑:程序博客网 时间:2024/06/05 04:52

http://blog.csdn.net/ab198604/article/details/19125419

个人见解,先谈谈浅拷贝与深拷贝之间的区别,区分它们的最大区别就是在调用完拷贝构造函数后,两个对象之间是否还存在一定的联系,如果两个对象能够完全独立,则说明是深拷贝,否则是浅拷贝。以各种教材的String类的深拷贝实现为例,下面进行说明。

        为了实现深拷贝操作,我们需要自己来构建拷贝构造函数,因为我们一旦构建了自己的拷贝构造函数,系统就不再提供默认的拷贝构造函数了。

        下面是String.h

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #ifndef  _STRING_H_  
  2. #define _STRING_H_  
  3. class String  
  4. {  
  5. public:  
  6.     String(char *str=""); //带一个参数的构造函数,可传入参数如“ABC”  
  7.     ~String();  
  8.   
  9.     void Display();  
  10.   
  11.     String( const String &other);    //拷贝构造函数  
  12.     String & operator=(const String &other);  //等号赋值运算符重载  
  13.   
  14. private:  
  15.     char *str_;          //String需要维护str_数据成员  
  16.     char *AllocAndCpy(char *str);  
  17. };  
  18.   
  19. #endif  
        

        下面是String类的具体实现,String.cpp

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include "String.h"  
  2. #include <cstring>  
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. char *String::AllocAndCpy(char *str) //实施深拷贝的函数  
  7. {  
  8.     int len = strlen(str) + 1;  
  9.     char *tmp = new char[len];  
  10.     memset( tmp, 0, len);  
  11.     strncpy(tmp, str, len );  
  12.     return tmp;  
  13. }  
  14.   
  15. String::String( const String & other) /* : str_(other.str_) 这种方式还是浅拷贝*/  
  16. {  
  17.         str_ = AllocAndCpy(other.str_); //函数内的实现才是真正的深拷贝,它的处理使得str_与原对象脱离了联系  
  18. }  
  19.   
  20. String & String::operator=(const String &other)  
  21. {  
  22.     ifthis == &other)  
  23.     {  
  24.         return *this;  
  25.     }  
  26.   
  27.     //销毁原有空间  
  28.     delete [] str_;  
  29.     str_ = AllocAndCpy(other.str_);  
  30.   
  31.     return *this;  
  32. }  
  33.   
  34. String::String( char *str)  
  35. {  
  36.     str_ = AllocAndCpy(str);  
  37. }  
  38.   
  39. String::~String()  
  40. {  
  41.     delete [] str_;  
  42. }  
  43.   
  44. void String::Display()  
  45. {  
  46.      cout << str_ << endl;  
  47. }  
        上面代码中的AllocAndCpy函数是非常重要的函数,它重新分配了一块内存空间,然后将原内存的数据复制至新内存空间中,这样两个类对象之间就有各自独立的内存空间,对象之间就没有联系,这就是深拷贝。如果不这么做,直接通过str_ = other.str_实施拷贝,只是简单的复制指针的地址,这样两个类对象实际指向的是同一块内存,当两上对象的生命周期结束后,均需要释放内存空间,这样就造成了同一块内存单元被释放了两次。因为它们仅仅拷贝了“形”,而没有拷贝“本质”的数据,所以拷贝构造函数需要我们自己来重新编写。由于等号赋值重载函数需要赋值操作,实际上也是复制,所以也需要自己编写operator=函数。

         下面是测试代码,代码中也有详细的注释:       

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int main(void)  
  2. {  
  3.     String s1("AAA");  
  4.     s1.Display();  
  5.       
  6.     String s2 = s1;//调用默认的拷贝构造函数,系统提供的默认拷贝构造函数实施的是浅拷贝s2.str_ = s1.str_;相同于s1,s2两个对象的str_指针指向相同的内存,当两个对象生存期结束时,都要调用析构函数,导致同一块内存被,释放了两次,故而产生错误.解决方法实施深拷贝,自己提供拷贝构造函数  
  7.   
  8.   
  9.     //等号运算符的重载  
  10.     String s3;  
  11.     s3.Display();  
  12.     s3 = s2; // = 号运算符,它调用的是系统默认的等号运算符,实施的也是浅拷贝,s3.str_ = s2.str_;仍然会出现同一块内存被销毁两次的情况,所以要自己提供等号运算符实施深拷贝。等价于s3.operator=(s2);  
  13.   
  14.     return 0;  
  15. }  
        

        下面说明关于禁止拷贝的情形。在某些场合,比如设计模式中有个Singleton单例模式,即一个类只能有一个实例,就是说这个类的对象是要独一无二的,它不允许被拷贝,也不允许赋值,也就是说我们要提供禁止拷贝的功能,以下就是将一个类实施为禁止拷贝的步骤,其实很简单:

        (1)将拷贝构造函数与operator=(等号运算符重载函数)均声明为private私有访问权限

        (2)在CPP文件中不提供他们的实现

        这样,当在主程序进行拷贝复制或赋值操作时,编译将出错,因为它们没有拷贝构造函数或赋值操作的实现。只有声明,而且还是私有的。


        下面总结一下,空类默认的成员函数有6个:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class Empty{}; //这是一个空类。  
  2.   
  3. Empty(); //默认构造函数  
  4. Empty(const Empty &);//默认拷贝构造  
  5. ~Empty(); //默认析构函数  
  6. Empty & operator=(const Empty &)//默认赋值运算符  
  7. Empty *operator&();//取地址运算符  
  8. const Empty *operator &() const//取地址运算符const  
        下面举例说明operator&()与operator &() const的用法:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Empty  
  5. {  
  6. public:  
  7.     Empty * operator&()  
  8.     {  
  9.         cout << "AAA"<< endl;  
  10.         return *this;  
  11.     }  
  12.   
  13.     const Empty *operator&() const  
  14.     {  
  15.         cout << "BBB"<<endl;  
  16.         return this;  
  17.     }  
  18.   
  19. };  
  20.   
  21. int main()  
  22. {  
  23.     Empty e;  
  24.     Empty *p = &e; //将调用取地址运算符,等价于e.operator&()  
  25.       
  26.     const Empty e2;  
  27.     const Empty *p2 = &e2; //调用的是含const的取地址运算符  
  28.   
  29.     //<strong><span style="color:#ff6666;">空类的大小是1个字节</span></strong>,编译器将为它生成一个字生的空间  
  30.     cout << sizoef(Empty) << endl;  
  31.   
  32.     return 0;  
  33. }  

0 0
原创粉丝点击