拷贝赋值运算符调用拷贝构造函数
bigfly Lv4

在C++中,拷贝赋值运算符和拷贝构造函数是两种不同的操作,用于对象的复制。了解这两者之间的区别以及它们的正确用法对于避免潜在的问题至关重要。

一、拷贝赋值运算符

拷贝赋值运算符(operator=)用于将一个对象的内容复制到另一个已经存在的对象中。

1
2
3
4
5
6
7
8
9
10
class MyClass {
public:
MyClass& operator=(const MyClass& other) {
if (this == &other) return *this; // 处理自赋值
// 释放当前对象的资源(如果有的话)
// 复制other对象的资源
return *this;
}
};

二、拷贝构造函数

拷贝构造函数用于创建一个新对象,并用已有对象初始化它。

1
2
3
4
5
6
7
class MyClass {
public:
MyClass(const MyClass& other) {
// 用other对象初始化新对象的资源
}
};

三、经验

令拷贝赋值操作符调用拷贝构造函数是不合理的,因为这就像试图构造一个已经存在的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <algorithm>

class MyClass {
private:
int* data;
size_t size;

public:
// 默认构造函数
MyClass(size_t s = 0) : size(s), data(s ? new int[s] : nullptr) {
std::cout << "Default constructor" << std::endl;
}

// 拷贝构造函数
MyClass(const MyClass& other) : size(other.size), data(other.size ? new int[other.size] : nullptr) {
std::copy(other.data, other.data + other.size, data);
std::cout << "Copy constructor" << std::endl;
}

// 错误的拷贝赋值运算符实现
MyClass& operator=(const MyClass& other) {
if (this != &other) {
// 错误地调用拷贝构造函数
MyClass temp(other);//给新对象调用的是构造函数
std::swap(size, temp.size);
std::swap(data, temp.data);
}
std::cout << "Copy assignment operator" << std::endl;
return *this;
}


//正确的实现
MyClass& operator=(const MyClass& other) {
if (this == &other) return *this; // 处理自赋值
delete[] data; // 释放当前对象的资源
data = new int[other.size]; // 分配新资源并复制
std::copy(other.data, other.data + other.size, data);
return *this;
}

// 析构函数
~MyClass() {
delete[] data;
}
};

int main() {
MyClass a(5); // 调用默认构造函数
MyClass b; // 调用默认构造函数
b = a; // 调用拷贝赋值运算符(错误实现)
return 0;
}

上述错误的拷贝赋值运算符分析

1 错误的实现:

  • 资源管理:在处理非自赋值情况时,它创建了一个临时对象 temp,然后交换了当前对象和临时对象的资源。但是,在自赋值情况下,这种实现会导致当前对象的资源被删除,然后尝试使用被删除的资源,这会导致未定义行为。

2 正确的实现:

  • 资源管理: 对于非自赋值情况,该实现删除当前对象的资源,然后重新分配内存,并复制来自 other 对象的数据。这确保了当前对象与 other 对象的数据相同,但是不共享内存。这样的实现在处理自赋值和非自赋值情况时都是安全的。

3总结

在错误的实现中,自赋值情况下会导致当前对象的资源被删除,是因为它创建了一个临时对象 temp,然后交换了当前对象和临时对象的资源。具体地说,以下是发生的情况:

  1. 当发生自赋值时,拷贝赋值运算符首先创建了一个临时对象 temp,该临时对象的数据与 other 对象相同。
  2. 然后,拷贝赋值运算符尝试交换当前对象和临时对象的资源。这意味着当前对象 this 的资源会被释放,而临时对象 temp 的资源会成为当前对象的资源。
  3. 但是,在自赋值的情况下,当前对象和临时对象都是同一个对象,即它们的地址相同。因此,交换操作实际上是将当前对象的资源指针(data)与自己交换,导致了问题。
  4. 由于临时对象 temp 和当前对象 this 是同一个对象,因此在交换后,当前对象的资源指针 data 实际上指向了一个已经被释放的内存空间,这会导致未定义行为,通常是内存错误、段错误或其他程序崩溃。

因此,这种错误的实现在自赋值情况下会导致当前对象的资源被删除,并尝试使用被删除的资源,从而导致了未定义行为。