C++编译器可能会给类添加四个函数:
- 默认构造函数,空实现。
- 默认析构函数,空实现。
- 默认拷贝构造函数,对成员变量进行浅拷贝。
- 默认赋值函数, 对成员变量进行浅拷贝。
对象的赋值运算是用一个已经存在的对象,给另一个已经存在的对象赋值。
如果类的定义中没有重载赋值函数,编译器就会提供一个默认赋值函数。
如果类中重载了赋值函数,编译器将不提供默认赋值函数。
重载赋值函数的语法:类名 & operator=(const 类名 & 源对象);
注意:
- 编译器提供的默认赋值函数,是浅拷贝。
- 如果对象中不存在堆区内存空间,默认赋值函数可以满足需求,否则需要深拷贝。
- 赋值运算和拷贝构造不同:拷贝构造是指原来的对象不存在,用已存在的对象进行构造;赋值运算是指已经存在了两个对象,把其中一个对象的成员变量的值赋给另一个对象的成员变量。
#include <iostream>
#include <cstring> // For strcpy and strlen
class String {
private:
char* data;
public:
// 默认构造函数
String() : data(nullptr) {}
// 参数化构造函数
String(const char* str) {
if (str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
} else {
data = nullptr;
}
}
// 拷贝构造函数
String(const String& other) {
if (other.data) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
} else {
data = nullptr;
}
}
// 赋值运算符
String& operator=(const String& other) {
if (this == &other) {
return *this; // 自赋值情况检查
}
delete[] data; // 先释放原有资源
if (other.data) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
} else {
data = nullptr;
}
return *this;
}
// 析构函数
~String() {
delete[] data;
}
// 打印字符串内容
void print() {
if (data) {
std::cout << data << std::endl;
} else {
std::cout << "String is empty" << std::endl;
}
}
};
int main() {
String s1("Hello");
String s2 = s1; // 调用拷贝构造函数
String s3;
s3 = s1; // 调用赋值运算符
std::cout << "s1: ";
s1.print();
std::cout << "s2: ";
s2.print();
std::cout << "s3: ";
s3.print();
return 0;
}
在这个例子中,我们定义了一个String
类来处理C风格字符串。我们实现了默认构造函数、参数化构造函数、拷贝构造函数、赋值运算符和析构函数。注意,赋值运算符中我们首先检查了自赋值的情况,然后释放了原有的内存,最后根据源对象的状态分配了新的内存并复制了数据。这保证了当发生赋值时,对象能够安全地复制数据并释放旧数据。