复制构造函数的用途就是解决:当将数据从一个对象复制到另一个对象时,其中的指针数据成员没有被正确处理的情况。

如:

1
2
3
4
5
6
7
8
9
10
struct Node
{
char *name;
int age;
Node(char *n=" ", int a = 0)
{
name = strdup(n); // strdup是用来分配一个新的内存并返回该指向该字符串的指针
age = a;
}
}

当这样带char *name指针的对象,经历复制时,会遇到以下问题

1
2
3
4
5
6
7
8
Node node1("node1", 10); // Create object node1
Node node2(node1); // Create a node1 copy named node, 同样可写为: Node node2 = node1;

strcpy(node2.name, "changedName"); // 将node2的name改为"changedName"
node2.age = 30; // 将node2的age改为30

printf("node1.name = %s, node1.age = %d\n", node1.name, node1.age);
// 输出是 node1.name = changedName, node1.age = 10

这里node1.name的值跟node2.name的值一样,是因为Node这个struct没有定义复制构造函数,导致编译器自己生成了复制构造函数。生成的复制构造函数只是逐个对成员进行复制,所以它将char *name上面指向name字符串的指针值,即地址,复制给了node2,而不是在地址上的字符串的值。而因为age本身就是一个值,所以没被影响。

所以这里需要创建复制构造函数来解决这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Node
{
char *name;
int age;
Node(char *n=" ", int a = 0)
{
name = strdup(n); // strdup是用来分配一个新的内存并返回该指向该字符串的指针
age = a;
}
// 创建复制构造函数
Node(const Node& n)
{
name = strdup(n.name); // 用strdup来分配新内存,储存新的指针来避免只复制指向同一个地址的指针
age = n.age;
}
}

这样node1, node2之间就不会有相互影响的问题。

同样,为了完全杜绝此类问题发生,还需重载复制运算符来应对node1 = node2这种复制方式。

1
2
3
4
5
6
7
8
9
10
11
Node& operator= (const Node& n)
{
if (this != &n) // 避免自己=自己还多走一步, this是一个指向自己(调用这个函数的调用对象)的指针,比如 a = b,调用对象就是a
{
if (name != 0) // 不为特殊地址/空地址
free(name); // 将指向的地址free掉
name = strdup(n.name); // 来储存新的字符串地址
age = n.age;
}
return *this;
}

所以当对象包含指针数据时,需要同时加上复制构造函数以及重载=运算符,这样所有情况就都解决了。