首页 > 应用开发 > IT综合 > C++类与对象深度剖析:6个默认成员函数详解
# C++类与对象深度剖析:6个默认成员函数详解
## 默认构造函数:对象的初始化起点
构造函数是对象创建的起点,编译器会在需要时自动生成默认构造函数。理解构造函数的各种形式对于编写健壮的C++代码至关重要。
### 默认构造函数的基础形式
```cpp
class Person {
private:
std::string name;
int age;
double height;
public:
// 1. 默认构造函数
Person() : name("Unknown"), age(0), height(0.0) {
std::cout << "默认构造函数被调用" << std::endl;
}
// 2. 带参数的构造函数
Person(const std::string& n, int a, double h)
: name(n), age(a), height(h) {
std::cout << "带参数构造函数被调用: " << name << std::endl;
}
// 3. 委托构造函数(C++11)
Person(const std::string& n) : Person(n, 0, 0.0) {
std::cout << "委托构造函数被调用" << std::endl;
}
// 4. 显式默认构造函数
Person() = default;
// 5. 显示删除构造函数
Person(const Person&) = delete; // 禁止拷贝构造
};
void constructor_demo() {
std::cout << "=== 构造函数演示 ===" << std::endl;
// 调用默认构造函数
Person p1;
// 调用带参数构造函数
Person p2("Alice", 25, 165.5);
// 调用委托构造函数
Person p3("Bob");
// 使用列表初始化(C++11)
Person p4{"Charlie", 30, 175.0};
// 动态分配
Person* p5 = new Person("David", 35, 180.0);
delete p5;
// 数组初始化
Person people[3] = {
Person("Eve", 28, 160.0),
Person("Frank", 32, 172.0),
Person() // 默认构造
};
}
```
### 成员初始化列表的重要性
```cpp
class ComplexClass {
private:
const int id; // const成员必须在初始化列表初始化
int& ref; // 引用成员必须在初始化列表初始化
std::vector<int> data; // 非内置类型应在初始化列表初始化
public:
// 正确的构造函数:使用初始化列表
ComplexClass(int id_value, int& ref_value, size_t size)
: id(id_value), ref(ref_value), data(size) {
// id = id_value; // 错误:const成员不能赋值
// ref = ref_value; // 错误:引用必须在初始化时绑定
// data.resize(size); // 可以,但效率低于初始化列表
}
// 错误的构造函数:在函数体内初始化
ComplexClass(int id_value, int& ref_value)
: id(id_value), ref(ref_value) { // 前两个成员正确初始化
data = std::vector<int>(100); // 先默认构造,再赋值
}
// 成员初始化顺序问题
class InitializationOrder {
int a;
int b;
public:
// 注意:初始化顺序只与成员声明顺序有关,与初始化列表顺序无关
InitializationOrder() : b(10), a(b + 1) {
// 危险:a初始化为 b+1,但b尚未初始化!
// a的值是未定义的
}
};
};
void initialization_list_demo() {
int external_value = 42;
ComplexClass obj(1, external_value, 10);
std::cout << "初始化列表演示完成" << std::endl;
}
```
## 析构函数:资源释放的守护者
析构函数在对象生命周期结束时自动调用,负责资源清理工作。
```cpp
class ResourceHolder {
private:
int* dynamic_array;
size_t size;
FILE* file_handle;
public:
// 构造函数
ResourceHolder(size_t arr_size, const char* filename)
: size(arr_size), file_handle(nullptr) {
// 动态分配数组
dynamic_array = new int[size];
std::cout << "分配了 " << size << " 个整数的数组" << std::endl;
// 打开文件
file_handle = fopen(filename, "w");
if (file_handle) {
std::cout << "打开了文件: " << filename << std::endl;
}
}
// 析构函数
~ResourceHolder() {
std::cout << "析构函数被调用" << std::endl;
// 释放动态数组
delete[] dynamic_array;
std::cout << "释放了动态数组" << std::endl;
// 关闭文件
if (file_handle) {
fclose(file_handle);
std::cout << "关闭了文件" << std::endl;
}
}
// 禁止拷贝(防止浅拷贝问题)
ResourceHolder(const ResourceHolder&) = delete;
ResourceHolder& operator=(const ResourceHolder&) = delete;
};
void destructor_demo() {
std::cout << "=== 析构函数演示 ===" << std::endl;
{
std::cout << "进入内部作用域" << std::endl;
ResourceHolder holder1(100, "test.txt");
// 动态分配对象
ResourceHolder* holder2 = new ResourceHolder(50, "temp.txt");
delete holder2; // 显式调用析构函数
std::cout << "离开内部作用域" << std::endl;
// holder1的析构函数在这里自动调用
}
std::cout << "回到外部作用域" << std::endl;
// 异常安全演示
try {
ResourceHolder holder3(200, "data.txt");
throw std::runtime_error("测试异常");
// holder3的析构函数在异常抛出后仍会被调用
} catch (const std::exception& e) {
std::cout << "捕获异常: " << e.what() << std::endl;
}
}
```
## 拷贝构造函数与拷贝赋值运算符
### 拷贝构造函数
```cpp
class String {
private:
char* data;
size_t length;
// 辅助函数:深拷贝实现
void deep_copy(const String& other) {
length = other.length;
data = new char[length + 1];
std::copy(other.data, other.data + length + 1, data);
}
public:
// 构造函数
String(const char* str = "") : length(0), data(nullptr) {
if (str) {
length = std::strlen(str);
data = new char[length + 1];
std::strcpy(data, str);
}
}
// 拷贝构造函数(深拷贝)
String(const String& other) : length(0), data(nullptr) {
std::cout << "拷贝构造函数被调用" << std::endl;
deep_copy(other);
}
// 移动构造函数(C++11)
String(String&& other) noexcept
: length(other.length), data(other.data) {
std::cout << "移动构造函数被调用" << std::endl;
other.length = 0;
other.data = nullptr;
}
// 析构函数
~String() {
delete[] data;
}
// 拷贝赋值运算符
String& operator=(const String& other) {
std::cout << "拷贝赋值运算符被调用" << std::endl;
if (this != &other) {
// 释放现有资源
delete[] data;
// 深拷贝新资源
deep_copy(other);
}
return *this;
}
// 移动赋值运算符(C++11)
String& operator=(String&& other) noexcept {
std::cout << "移动赋值运算符被调用" << std::endl;
if (this != &other) {
delete[] data;
length = other.length;
data = other.data;
other.length = 0;
other.data = nullptr;
}
return *this;
}
// 显示内容
void display() const {
if (data) {
std::cout << data;
}
}
};
void copy_constructor_demo() {
std::cout << "=== 拷贝构造函数演示 ===" << std::endl;
// 1. 直接初始化(调用拷贝构造)
String s1("Hello");
String s2 = s1; // 拷贝构造函数
// 2. 函数参数传递(调用拷贝构造)
auto print_string = [](String str) {
str.display();
std::cout << std::endl;
};
print_string(s1); // 拷贝构造函数
// 3. 函数返回值优化(可能调用拷贝构造或移动构造)
auto create_string = []() -> String {
String temp("World");
return temp; // 可能调用移动构造(C++11及以上)
};
String s3 = create_string();
// 4. 容器操作(调用拷贝构造)
std::vector<String> strings;
strings.push_back(s1); // C++11前:拷贝构造
strings.emplace_back("C++"); // C++11:直接构造
// 5. 拷贝赋值运算符
String s4("Original");
s4 = s1; // 拷贝赋值运算符
}
```
### 拷贝赋值运算符的现代实现
```cpp
class ModernString {
private:
char* data;
size_t length;
public:
ModernString(const char* str = "") : length(0), data(nullptr) {
if (str) {
length = std::strlen(str);
data = new char[length + 1];
std::strcpy(data, str);
}
}
// 拷贝赋值运算符的现代实现(拷贝交换惯用法)
ModernString& operator=(ModernString other) { // 注意:按值传递
std::cout << "拷贝赋值运算符(拷贝交换)" << std::endl;
swap(*this, other);
return *this;
}
// 移动赋值运算符
ModernString& operator=(ModernString&& other) noexcept {
std::cout << "移动赋值运算符" << std::endl;
if (this != &other) {
delete[] data;
length = other.length;
data = other.data;
other.length = 0;
other.data = nullptr;
}
return *this;
}
// 友元swap函数
friend void swap(ModernString& first, ModernString& second) noexcept {
using std::swap;
swap(first.length, second.length);
swap(first.data, second.data);
}
~ModernString() {
delete[] data;
}
};
```
## 移动构造函数与移动赋值运算符(C++11)
移动语义允许资源所有权的转移,避免不必要的深拷贝。
```cpp
class Buffer {
private:
int* data;
size_t size;
public:
// 构造函数
Buffer(size_t s) : size(s), data(new int[s]) {
std::cout << "构造Buffer,大小: " << size << std::endl;
}
// 拷贝构造函数
Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {
std::cout << "拷贝构造Buffer" << std::endl;
std::copy(other.data, other.data + size, data);
}
// 移动构造函数(noexcept很重要)
Buffer(Buffer&& other) noexcept
: size(other.size), data(other.data) {
std::cout << "移动构造Buffer" << std::endl;
other.size = 0;
other.data = nullptr;
<"api.q6k0.org.cn"><"bs.q6k0.org.cn"><"tv.q6k0.org.cn">
}
// 拷贝赋值运算符
Buffer& operator=(const Buffer& other) {
std::cout << "拷贝赋值Buffer" << std::endl;
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
}
return *this;
}
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
std::cout << "移动赋值Buffer" << std::endl;
if (this != &other) {
delete[] data;
size = other.size;
data = other.data;
other.size = 0;
other.data = nullptr;
}
return *this;
}
// 析构函数
~Buffer() {
std::cout << "销毁Buffer,大小: " << size << std::endl;
delete[] data;
}
// 模拟资源密集型操作
void fill_with_random() {
for (size_t i = 0; i < size; ++i) {
data[i] = rand() % 100;
}
}
};
void move_semantics_demo() {
std::cout << "=== 移动语义演示 ===" << std::endl;
// 创建大对象
Buffer buffer1(1000000);
buffer1.fill_with_random();
// 移动构造演示
Buffer buffer2 = std::move(buffer1); // 移动构造
std::cout << "移动后,buffer1大小: " << buffer1.size << std::endl;
// 移动赋值演示
Buffer buffer3(500000);
buffer3 = std::move(buffer2); // 移动赋值
// 容器中的移动语义
std::vector<Buffer> buffers;
buffers.reserve(3);
// push_back的移动优化
Buffer temp1(100);
buffers.push_back(std::move(temp1)); // 移动构造
// emplace_back直接构造
buffers.emplace_back(200);
// 返回值优化 + 移动语义
auto create_buffer = []() -> Buffer {
Buffer local(300);
local.fill_with_random();
return local; // 可能触发NRVO或移动构造
};
Buffer buffer4 = create_buffer();
// std::unique_ptr演示移动语义
std::unique_ptr<Buffer> ptr1 = std::make_unique<Buffer>(400);
std::unique_ptr<Buffer> ptr2 = std::move(ptr1); // 所有权转移
std::cout << "移动语义演示完成" << std::endl;
}
```
## 默认成员函数的生成规则
### 特殊成员函数的生成条件
```cpp
class RuleDemo {
private:
int value;
std::string name;
public:
// 情况1:如果用户声明了任何构造函数,编译器不再生成默认构造函数
RuleDemo(int v) : value(v) {}
// RuleDemo() = default; // 需要显式声明
// 情况2:如果用户声明了拷贝构造函数,编译器不再生成移动构造函数
RuleDemo(const RuleDemo& other) : value(other.value), name(other.name) {
std::cout << "用户定义拷贝构造" << std::endl;
}
// 情况3:如果用户声明了拷贝赋值运算符,编译器不再生成移动赋值运算符
RuleDemo& operator=(const RuleDemo& other) {
if (this != &other) {
value = other.value;
name = other.name;
}
std::cout << "用户定义拷贝赋值" << std::endl;
return *this;
}
// 情况4:如果用户声明了析构函数,编译器仍会生成拷贝操作,但C++11后不推荐
~RuleDemo() {
std::cout << "用户定义析构函数" << std::endl;
}
// 情况5:如果用户声明了移动构造函数或移动赋值运算符,编译器会删除拷贝操作
// RuleDemo(RuleDemo&&) = delete; // 这会导致拷贝操作被删除
};
class ModernRule {
private:
std::unique_ptr<int> resource; // 不可拷贝的成员
public:
// 用户声明了移动构造函数
ModernRule(ModernRule&& other) noexcept = default;
// 结果:拷贝构造函数被隐式删除
// ModernRule(const ModernRule&) = delete;
// 用户声明了移动赋值运算符
ModernRule& operator=(ModernRule&& other) noexcept = default;
// 结果:拷贝赋值运算符被隐式删除
// ModernRule& operator=(const ModernRule&) = delete;
};
void generation_rules_demo() {
std::cout << "=== 默认函数生成规则演示 ===" << std::endl;
RuleDemo obj1(10);
// RuleDemo obj2; // 错误:没有默认构造函数
RuleDemo obj3(obj1); // 调用用户定义的拷贝构造
RuleDemo obj4 = obj1; // 调用用户定义的拷贝构造
// 测试移动语义
ModernRule m1;
// ModernRule m2 = m1; // 错误:拷贝构造被删除
ModernRule m3 = std::move(m1); // 正确:移动构造可用
}
```
## 综合示例:实现一个完整的类
```cpp
class Matrix {
private:
double** data;
size_t rows;
size_t cols;
// 辅助函数:分配内存
void allocate_memory(size_t r, size_t c) {
rows = r;
cols = c;
data = new double*[rows];
for (size_t i = 0; i < rows; ++i) {
data[i] = new double[cols](); // 值初始化为0
}
}
// 辅助函数:释放内存
void free_memory() noexcept {
if (data) {
for (size_t i = 0; i < rows; ++i) {
delete[] data[i];
}
delete[] data;
data = nullptr;
}
rows = cols = 0;
}
// 辅助函数:深拷贝
void deep_copy(const Matrix& other) {
allocate_memory(other.rows, other.cols);
for (size_t i = 0; i < rows; ++i) {
std::copy(other.data[i], other.data[i] + cols, data[i]);
}
}
public:
// 1. 默认构造函数
Matrix() : data(nullptr), rows(0), cols(0) {
std::cout << "默认构造Matrix" << std::endl;
}
// 2. 带参数的构造函数
Matrix(size_t r, size_t c) : data(nullptr), rows(0), cols(0) {
std::cout << "构造 " << r << "x" << c << " Matrix" << std::endl;
allocate_memory(r, c);
}
// 3. 拷贝构造函数
Matrix(const Matrix& other) : data(nullptr), rows(0), cols(0) {
std::cout << "拷贝构造Matrix" << std::endl;
deep_copy(other);
}
// 4. 移动构造函数(noexcept很重要)
Matrix(Matrix&& other) noexcept
: data(other.data), rows(other.rows), cols(other.cols) {
std::cout << "移动构造Matrix" << std::endl;
other.data = nullptr;
other.rows = other.cols = 0;
}
// 5. 拷贝赋值运算符(异常安全)
Matrix& operator=(const Matrix& other) {
std::cout << "拷贝赋值Matrix" << std::endl;
if (this != &other) {
// 拷贝交换惯用法
Matrix temp(other); // 可能抛出异常
swap(*this, temp); // noexcept操作
}
return *this;
}
// 6. 移动赋值运算符
Matrix& operator=(Matrix&& other) noexcept {
std::cout << "移动赋值Matrix" << std::endl;
if (this != &other) {
free_memory(); // noexcept
data = other.data;
rows = other.rows;
cols = other.cols;
other.data = nullptr;
other.rows = other.cols = 0;
}
return *this;
}
// 7. 析构函数
~Matrix() noexcept {
std::cout << "销毁Matrix " << rows << "x" << cols << std::endl;
<"cba.q6k0.org.cn"><"sjXqr.q6k0.org.cn"><"read.q6k0.org.cn">
free_memory();
}
// 友元swap函数
friend void swap(Matrix& first, Matrix& second) noexcept {
using std::swap;
swap(first.data, second.data);
swap(first.rows, second.rows);
swap(first.cols, second.cols);
}
// 访问元素(const和非const版本)
double& at(size_t i, size_t j) {
if (i >= rows || j >= cols) {
throw std::out_of_range("Matrix索引越界");
}
return data[i][j];
}
const double& at(size_t i, size_t j) const {
if (i >= rows || j >= cols) {
throw std::out_of_range("Matrix索引越界");
}
return data[i][j];
}
// 获取维度
size_t getRows() const { return rows; }
size_t getCols() const { return cols; }
// 矩阵加法
Matrix operator+(const Matrix& other) const {
if (rows != other.rows || cols != other.cols) {
throw std::invalid_argument("矩阵维度不匹配");
}
Matrix result(rows, cols);
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < cols; ++j) {
result.data[i][j] = data[i][j] + other.data[i][j];
}
}
return result;
}
};
void matrix_class_demo() {
std::cout << "=== 完整Matrix类演示 ===" << std::endl;
try {
// 创建矩阵
Matrix m1(2, 3);
m1.at(0, 0) = 1.0; m1.at(0, 1) = 2.0; m1.at(0, 2) = 3.0;
m1.at(1, 0) = 4.0; m1.at(1, 1) = 5.0; m1.at(1, 2) = 6.0;
// 拷贝构造
Matrix m2 = m1;
// 移动构造
Matrix m3 = std::move(Matrix(2, 3));
// 拷贝赋值
Matrix m4(1, 1);
m4 = m1;
// 移动赋值
Matrix m5;
m5 = Matrix(3, 2);
// 矩阵运算
Matrix sum = m1 + m2;
// 容器中的Matrix
std::vector<Matrix> matrices;
matrices.reserve(3);
matrices.push_back(std::move(m1)); // 移动构造
matrices.emplace_back(2, 2); // 直接构造
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
}
std::cout << "演示完成" << std::endl;
}
```
## 最佳实践总结
### 默认成员函数的设计原则
1. **遵循三五/零法则**
```cpp
// 三五法则:如果需要自定义析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,通常需要定义所有三个
class RuleOfThree {
public:
~RuleOfThree(); // 自定义析构函数
RuleOfThree(const RuleOfThree&); // 自定义拷贝构造
RuleOfThree& operator=(const RuleOfThree&); // 自定义拷贝赋值
};
// 五法则:C++11增加移动操作
class RuleOfFive {
public:
~RuleOfFive(); // 自定义析构函数
RuleOfFive(const RuleOfFive&); // 自定义拷贝构造
RuleOfFive& operator=(const RuleOfFive&); // 自定义拷贝赋值
RuleOfFive(RuleOfFive&&); // 自定义移动构造
RuleOfFive& operator=(RuleOfFive&&); // 自定义移动赋值
};
// 零法则:如果不需要自定义任何特殊成员函数,让编译器生成它们
class RuleOfZero {
// 编译器生成所有默认成员函数
};
```
2. **移动操作的noexcept声明**
```cpp
class ResourceHolder {
public:
// 移动操作应声明为noexcept,以便在标准库中优化
ResourceHolder(ResourceHolder&& other) noexcept;
ResourceHolder& operator=(ResourceHolder&& other) noexcept;
};
```
3. **使用=default和=delete**
```cpp
class SmartDefaults {
public:
SmartDefaults() = default; // 显式默认构造
SmartDefaults(const SmartDefaults&) = delete; // 禁止拷贝
SmartDefaults& operator=(const SmartDefaults&) = delete;
SmartDefaults(SmartDefaults&&) = default; // 默认移动
};
```
### 资源管理建议
1. **使用RAII管理资源**
```cpp
class FileHandle {
FILE* handle;
public:
explicit FileHandle(const char* filename)
: handle(fopen(filename, "r")) {
if (!handle) throw std::runtime_error("无法打开文件");
}
~FileHandle() { if (handle) fclose(handle); }
// 禁用拷贝,启用移动
FileHandle(const FileHandle&) = delete;
FileHandle(FileHandle&& other) noexcept
: handle(other.handle) {
other.handle = nullptr;
}
};
```
2. **优先使用初始化列表**
```cpp
class ProperInitialization {
const int id;
std::vector<int> data;
public:
ProperInitialization(int id_val, size_t size)
: id(id_val), data(size) { // 正确:初始化列表
// 不要在这里初始化const成员或引用
}
};
```
通过深入理解C++的6个默认成员函数,开发者可以编写出更安全、更高效的类设计。这些函数共同定义了对象的创建、复制、移动和销毁行为,是C++面向对象编程的基石。掌握它们的实现原理和最佳实践,对于构建健壮的C++应用程序至关重要。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3106783/ ,如需转载,请注明出处,否则将追究法律责任。