1. 理解C++中的对象赋值操作
在C++面向对象编程中,对象间的赋值操作远比基本数据类型复杂得多。我刚接触这个概念时,曾经因为不理解深浅赋值的区别而写出过导致程序崩溃的代码。让我们从一个实际案例开始:
假设我们有一个简单的Person类,其中包含一个指向姓名的指针成员:
cpp复制class Person {
public:
char* name;
int age;
Person(const char* n, int a) {
age = a;
name = new char[strlen(n) + 1];
strcpy(name, n);
}
~Person() {
delete[] name;
}
};
当执行Person p2 = p1;这样的赋值操作时,编译器默认会执行浅赋值(shallow copy),也就是简单地将p1的成员变量值复制给p2。对于普通类型如int,这没有问题;但对于指针成员,这会导致两个对象的name指针指向同一块内存。
关键提示:浅赋值最大的危险在于,当这两个对象被销毁时,它们的析构函数会尝试释放同一块内存两次,这必然导致程序崩溃。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 浅赋值的本质与风险
2.1 浅赋值的工作原理
浅赋值的核心特点是"按位拷贝"(bitwise copy)。编译器生成的默认拷贝赋值运算符会:
- 将源对象的每个非静态成员变量直接复制到目标对象
- 对于指针成员,仅复制指针值(内存地址),不复制指针指向的内容
- 不进行任何内存分配操作
这种机制在处理简单数据类型时效率很高,但在处理动态分配的资源时极其危险。
2.2 浅赋值的典型问题场景
让我们通过一个更完整的例子来看浅赋值可能导致的三大问题:
cpp复制class StringBuffer {
public:
char* buffer;
size_t length;
StringBuffer(const char* str) {
length = strlen(str);
buffer = new char[length + 1];
strcpy(buffer, str);
}
~StringBuffer() {
delete[] buffer;
}
// 注意:这里没有重载赋值运算符
};
int main() {
StringBuffer sb1("Hello");
StringBuffer sb2("World");
sb2 = sb1; // 浅赋值发生
// 问题1:数据污染
strcpy(sb1.buffer, "Changed");
cout << sb2.buffer; // 输出"Changed",而非预期的"Hello"
// 问题2:内存泄漏
// sb2原来的"World"字符串内存永远无法释放
// 问题3:重复释放
// 程序结束时,sb1和sb2会尝试释放同一块内存
return 0;
}
2.3 浅赋值的适用场景
浅赋值并非完全无用,它在以下情况下是安全的:
- 类不包含任何指针成员
- 类不管理任何动态分配的资源
- 类成员都是基本数据类型或具有正确拷贝语义的对象
例如,下面这个Point类使用浅赋值完全安全:
cpp复制class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
3. 实现安全的深赋值
3.1 深赋值的核心原则
深赋值(deep copy)的核心思想是:对于指针成员,不仅要复制指针本身,还要复制指针指向的内容。这意味着:
