C++复制构造函数及三法则

来源:互联网 发布:写攻略的软件 编辑:程序博客网 时间:2024/05/17 06:46

C++复制构造函数及三法则

写在前面

             本节这个精辟的例子摘自《数据结构与算法-C++版》第3版 清华出版社一书。

             这里对该书的例子进行了整理,以帮助理解拷贝构造函数及C++三大法则。


结果为什么是这样的呢?

首先看下面例子代码。

//复制构造函数引起的错误#include <iostream>#include <string.h>using namespace std;struct Node{char *name;int age;//构造函数Node(char *n="",int a=0){   name = strdup(n);   age = a;}};int main(){   Node node1("Roger",20),node2(node1);   strcpy(node2.name,"Wendy");   node2.age = 30;   cout << node1.name<<" "<<node1.age   <<" "<<node2.name<<" "<<node2.age<<endl;}

例子中使用的strdup函数请参考文章末尾的补充说明部分的介绍。

这个例子的运行结果是  Roger 20 Wendy 30 ,对吗?

真正的结果是: Wendy 20 Wendy 30

影响结果的是因为在执行node2(node1)时调用了合成的复制构造函数,这个构造函数,执行的行为是:逐个成员初始化,将新对象初始化为原对象的副本。因此,实际上执行该语句后,node2与node1的name指向同一块内存区域,因此在strcpy(node2.name,"Wendy")后,两者的内容相同,而int型则执行简单赋值没有出错。

解决办法:  增加赋值构造函数

 //复制构造函数    Node(const Node& node){   name = strdup(node.name);   age = node.age;}

结果为什么又是这样的呢?

再看下面的例子:

//赋值操作符引起的错误#include <iostream>#include <string.h>using namespace std;struct Node{char *name;int age;//构造函数Node(char *n="",int a=0){   name = strdup(n);   age = a;} //复制构造函数    Node(const Node& node){   name = strdup(node.name);   age = node.age;   cout<<"copy constructor"<<endl;}};int main(){   Node node1("Roger",20),node2;   node2 = node1;   strcpy(node2.name,"Wendy");   node2.age = 30;   cout << node1.name<<" "<<node1.age   <<" "<<node2.name<<" "<<node2.age<<endl;}

这个例子的运行结果应该是  Roger 20 Wendy 30 了吧?

错了,真正的结果还是: Wendy 20 Wendy 30

影响结果的是因为在执行node2=node1时调用了合成的赋值操作符,执行的行为是:逐个成员赋值,右操作数对象的每个成员赋值给左操作数对象的对应成员。因此,实际上执行该语句后,node2与node1的name仍然指向同一块内存区域,因此在strcpy(node2.name,"Wendy")后,两者的内容相同,而int型则执行简单赋值没有出错。

解决办法:  增加赋值操作符重载函数

//重载赋值操作符Node& operator = (const Node& node){    if(this != &node){  if(name != NULL)     delete[] name;  name = strdup(node.name);      age = node.age;}return *this;}


这样程序才能如期工作。但是还有一点需要注意,程序中使用strdup分配的内存并没有释放,因此还需要添加析构函数。

//析构函数~Node(){   if(name != NULL) delete[] name;}

完整的代码如下:

//如果一个类需要析构函数,则它也需要赋值操作符和复制构造函数(三法则)#include <iostream>#include <string.h>using namespace std;struct Node{char *name;int age;//构造函数Node(char *n="",int a=0){   name = strdup(n);   age = a;}    //复制构造函数    Node(const Node& node){   name = strdup(node.name);   age = node.age;}//重载赋值操作符Node& operator = (const Node& node){    if(this != &node){  if(name != NULL)     delete[] name;  name = strdup(node.name);      age = node.age;}return *this;}//析构函数~Node(){   if(name != NULL) delete[] name;}};int main(){   Node node1("Roger",20),node2(node1);   strcpy(node2.name,"Wendy");   node2.age = 30;   cout << node1.name<<" "<<node1.age   <<" "<<node2.name<<" "<<node2.age<<endl;}

这里我们要强调的三法则,即是如果一个类需要析构函数,则它也需要赋值操作符和复制构造函数。

在c++ 11中三法则演变成了五法则,具体可以参考:Rule of three (C++ programming)


补充说明:

函数strdup

头文件:#include <string.h>

定义函数:char * strdup(const char *s);

函数说明:strdup()会先用maolloc()配置与参数s 字符串相同的空间大小,然后将参数s 字符串的内容复制到该内存地址,然后把该地址返回。该地址最后可以利用free()来释放。

返回值:返回一字符串指针,该指针指向复制后的新字符串地址。若返回NULL 表示内存不足。(摘自:C语言strdup()函数:复制字符串)

它的实现,类似于:

char *strdup (const char *s) {    char *d = malloc (strlen (s) + 1);   // Space for length plus nul    if (d == NULL) return NULL;          // No memory    strcpy (d,s);                        // Copy the characters    return d;                            // Return the new string}
0 0
原创粉丝点击