C++之类的构造与析构(三)

来源:互联网 发布:前段优化 编辑:程序博客网 时间:2024/05/22 11:45

类的赋值操作

        前文我们说到,在什么情况下我们需要自定义复制构造函数。现在我们再来讲讲类的赋值操作是否也需要重载。我们先来看个例子。

  1: #include <iostream>   
  2: using namespace std;   
  3:  
  4: class Test   
  5: {   
  6: public: 
  7:  
  8:     Test(char* p) 
  9:     { 
 10:         cout<<"This is a override constructor."<<endl; 
 11:         if(p) 
 12:         { 
 13:             a=new char[strlen(p)+1]; 
 14:             strcpy(a,p); 
 15:         }else 
 16:         { 
 17:             a=new char[1]; 
 18:             *a='\0'; 
 19:         } 
 20:     } 
 21:  
 22:     Test(Test const &t) 
 23:     { 
 24:         cout<<"This is a copy constructor."<<endl; 
 25:         if (t.a) 
 26:         { 
 27:             a=new char[strlen(t.a)+1]; 
 28:             strcpy(a,t.a); 
 29:         }else 
 30:         { 
 31:             a=new char[1]; 
 32:             *a='\0'; 
 33:         } 
 34:     } 
 35:  
 36:     ~Test() 
 37:     { 
 38:         delete []a; 
 39:     } 
 40:  
 41: public: 
 42:     char* a; 
 43: };   
 44:  
 45: int main()   
 46: {   
 47:     Test *t1=new Test("Hello, C++!"); 
 48:     cout<<"t1:"<<t1->a<<endl; 
 49:     Test t2("Hello,t1!"); 
 50:     cout<<"t2:"<<t2.a<<endl; 
 51:     t2=*t1; 
 52:     delete t1; 
 53:     cout<<"t2:"<<t2.a<<endl; 
 54:     return 0;   
 55: } 
 56:  
 57: 

        得到的结果如下,可以看出,出现野指针了……

2011-06-28_205545

        我们知道,默认情况下,编译器编译时会自动生成一个复制操作的实现,完成所有数据成员的值拷贝。那么我们就能看出上面例子的问题了。第一,野指针的问题;第二,复制后会有内存不释放的现象。我们简单地画个示意图,表示其中存在的问题。

=赋值操作

图1 执行“t2=*t1;”前

=赋值操作执行后

图2 执行“t2=*t1;”后

        从图1和图2的对比中我们可以看出,执行“t2=*t1;”后,t2中的a指针指向了内存1,内存2没有被释放。当执行完“delete t1; ”后,内存1被释放掉,t2的a指针称为野指针。既然我们知道问题是如何产生的,那么修改上面的代码以解决这些问题。新的代码如下:

  1: #include <iostream>   
  2: using namespace std;   
  3:  
  4: class Test   
  5: {   
  6: public: 
  7:  
  8:     Test(char* p) 
  9:     { 
 10:         cout<<"This is a override constructor."<<endl; 
 11:         if(p) 
 12:         { 
 13:             a=new char[strlen(p)+1]; 
 14:             strcpy(a,p); 
 15:         }else 
 16:         { 
 17:             a=new char[1]; 
 18:             *a='\0'; 
 19:         } 
 20:     } 
 21:  
 22:     Test(Test const &t) 
 23:     { 
 24:         cout<<"This is a copy constructor."<<endl; 
 25:         if (t.a) 
 26:         { 
 27:             a=new char[strlen(t.a)+1]; 
 28:             strcpy(a,t.a); 
 29:         }else 
 30:         { 
 31:             a=new char[1]; 
 32:             *a='\0'; 
 33:         } 
 34:     } 
 35:  
 36:     //重载=操作符 
 37:     Test& operator=(Test const& t) 
 38:     { 
 39:         cout<<"This is a operator=."<<endl; 
 40:         if (this==&t) 
 41:         { 
 42:             return *this; 
 43:         } 
 44:         if (t.a) 
 45:         { 
 46:             if (a) 
 47:             { 
 48:                 delete []a; 
 49:             } 
 50:             a=new char[strlen(t.a)+1]; 
 51:             strcpy(a,t.a); 
 52:         }else 
 53:         { 
 54:             a=new char[1]; 
 55:             *a='\0'; 
 56:         } 
 57:         return *this; 
 58:     } 
 59:  
 60:     ~Test() 
 61:     { 
 62:         delete []a; 
 63:     } 
 64:  
 65: public: 
 66:     char* a; 
 67: };   
 68:  
 69: int main()   
 70: {   
 71:     Test *t1=new Test("Hello, C++!"); 
 72:     cout<<"t1:"<<t1->a<<endl; 
 73:     Test t2("Hello,t1!"); 
 74:     cout<<"t2:"<<t2.a<<endl; 
 75:     t2=*t1; 
 76:     delete t1; 
 77:     cout<<"t2:"<<t2.a<<endl; 
 78:     return 0;   
 79: } 
 80:  
 81: 

        我们再来看看运行结果。

2011-06-28_205955

        这次的运行结果是正确的,重载的=操作符使得t2重新申请了空间,存放数据。当t1被删除后,t2的数据并没有被删除。好了,写到这,大家应该清楚重载=操作符的重要性了吧。

        还需要补充的两点。

1 “Test t2=t1;”是属于类的赋值还是属于类的复制构造?

属于类的复制构造。各位可以用上面的代码试一试嘛。额外添加“Test t3=t2;cout<<"t3:"<<t3.a<<endl;”,得到的结果是

2011-06-28_211624

2 “Test t2=t1;”和“Test t2;t2=t1;”谁的效率更高?

//调用类的复制构造函数实例化对象t2

Test t2=t1;

//调用类的默认构造函数实例化对象t2,再调用复制操作

Test t2;

t2=t1;

        最后,我们得到的结论是在含有指针数据成员的时候,类的赋值和复制构造函数必须同时设计,尽量用复制构造函数来代替赋值

感谢:《C++编程关键路径——程序员求职指南》

0 0