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

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()函数:复制字符串)

人之所以有一张嘴,而有两只耳朵,原因是听的要比说的多一倍。

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

相关文章:

你感兴趣的文章:

标签云: