C++对象构造、赋值、拷贝与析构详解
1. 构造函数
构造函数是用于初始化对象的函数,在创建对象时自动调用。
特点:
- 名称与类名相同,无返回值。
- 可以重载,允许多个构造函数。
- 可以有默认值,也可以无参数(默认构造函数)。
代码示例:
class Test {
public:
int x, y;
// 默认构造函数
Test() : x(0), y(0) {
cout << "Default Constructor called" << endl;
}
// 带参数的构造函数
Test(int a, int b) : x(a), y(b) {
cout << "Parameterized Constructor called" << endl;
}
};
int main() {
Test t1; // 调用默认构造函数
Test t2(10, 20); // 调用带参数的构造函数
return 0;
}
输出:
Default Constructor called
Parameterized Constructor called
2. 拷贝构造函数
用于通过一个已存在的对象创建一个新对象。
特点:
- 默认的拷贝构造函数执行浅拷贝(简单复制数据成员的值)。
- 如果类包含指针或动态分配的资源,建议定义深拷贝。
代码示例(深拷贝实现):
class Test {
private:
int* data;
public:
// 带参数构造函数
Test(int value) {
data = new int(value);
cout << "Constructor called for value: " << *data << endl;
}
// 拷贝构造函数
Test(const Test& obj) {
data = new int(*obj.data); // 深拷贝
cout << "Copy Constructor called" << endl;
}
// 析构函数
~Test() {
delete data;
cout << "Destructor called" << endl;
}
};
int main() {
Test t1(10); // 调用构造函数
Test t2 = t1; // 调用拷贝构造函数
return 0;
}
输出:
Constructor called for value: 10
Copy Constructor called
Destructor called
Destructor called
3. 赋值操作符重载
用于将一个对象的值赋给另一个已存在的对象。
区别:
- 拷贝构造函数:在创建新对象时调用。
- 赋值操作符:在对象已经存在时调用。
代码示例:
class Test {
private:
int* data;
public:
// 构造函数
Test(int value) {
data = new int(value);
cout << "Constructor called for value: " << *data << endl;
}
// 拷贝构造函数
Test(const Test& obj) {
data = new int(*obj.data);
cout << "Copy Constructor called" << endl;
}
// 赋值操作符重载
Test& operator=(const Test& obj) {
if (this == &obj) return *this; // 防止自赋值
delete data; // 释放旧资源
data = new int(*obj.data); // 深拷贝
cout << "Assignment Operator called" << endl;
return *this;
}
// 析构函数
~Test() {
delete data;
cout << "Destructor called" << endl;
}
};
int main() {
Test t1(10); // 调用构造函数
Test t2(20); // 调用构造函数
t2 = t1; // 调用赋值操作符
return 0;
}
输出:
Constructor called for value: 10
Constructor called for value: 20
Assignment Operator called
Destructor called
Destructor called
4. 静态对象
静态对象在程序生命周期内只初始化一次,其析构函数会在程序结束时调用。
代码示例:
class Test {
public:
Test() { cout << "Constructor called" << endl; }
~Test() { cout << "Destructor called" << endl; }
};
int main() {
static Test t; // 静态对象
cout << "Main function ends" << endl;
return 0;
}
输出:
Constructor called
Main function ends
Destructor called
5. 动态分配与内存管理
动态分配允许在运行时分配内存,但需手动释放。
代码示例:
class Test {
public:
Test() { cout << "Constructor called" << endl; }
~Test() { cout << "Destructor called" << endl; }
};
int main() {
Test* obj = new Test(); // 动态分配单个对象
delete obj; // 释放单个对象
Test* arr = new Test[2]; // 动态分配对象数组
delete[] arr; // 释放对象数组
return 0;
}
输出:
Constructor called
Constructor called
Constructor called
Destructor called
Destructor called
Destructor called
6. 临时对象与优化
- 临时对象:
- 在表达式中创建的对象,生命周期短。
- 常用于函数返回值或强制类型转换。
代码示例:
class Test {
public:
Test(int value) { cout << "Constructor called for value: " << value << endl; }
~Test() { cout << "Destructor called" << endl; }
};
int main() {
Test t1 = Test(10); // 临时对象
return 0;
}
输出:
Constructor called for value: 10
Destructor called
- 优化临时对象:
- 使用引用传递避免拷贝:
void func(const Test& obj);
- 使用移动语义优化临时对象(C++11):
Test(Test&& obj) noexcept { /* 转移资源 */ }
- 使用引用传递避免拷贝:
函数调用中的对象行为
- 示例:
Test getObject(Test t) { Test tmp(t.data); return tmp; } Test t2 = getObject(t1);
- 背后调用顺序:
t1
构造。- 实参传递调用拷贝构造。
- 局部对象
tmp
调用构造函数。 - 返回临时对象调用拷贝构造。
- 临时对象赋值给
t2
调用赋值运算符。 - 局部对象和临时对象析构。
注意事项
- 禁止返回局部对象的指针或引用:
- 局部对象在函数结束时被销毁,返回其地址会导致悬空指针。
- 优化建议:
- 使用引用传递避免拷贝。
- 避免不必要的临时对象生成。
7. 完整示例
以下是一个综合示例,展示对象的构造、拷贝、赋值、动态分配与析构:
#include <iostream>
using namespace std;
class Test {
private:
int* data;
public:
// 构造函数
Test(int value = 0) {
data = new int(value);
cout << "Constructor called for value: " << *data << endl;
}
// 拷贝构造函数
Test(const Test& obj) {
data = new int(*obj.data);
cout << "Copy Constructor called" << endl;
}
// 赋值操作符
Test& operator=(const Test& obj) {
if (this == &obj) return *this; // 防止自赋值
delete data;
data = new int(*obj.data);
cout << "Assignment Operator called" << endl;
return *this;
}
// 析构函数
~Test() {
delete data;
cout << "Destructor called" << endl;
}
};
int main() {
Test t1(10); // 构造函数
Test t2 = t1; // 拷贝构造函数
Test t3; // 默认构造函数
t3 = t1; // 赋值操作符
Test* p = new Test(20); // 动态分配
delete p; // 释放动态分配的内存
return 0;
}
输出:
Constructor called for value: 10
Copy Constructor called
Constructor called for value: 0
Assignment Operator called
Constructor called for value: 20
Destructor called
Destructor called
Destructor called
Destructor called
8. 总结
- 构造函数:用于对象初始化,可以有参数或默认值。
- 拷贝构造函数:用于复制对象,推荐实现深拷贝。
- 赋值操作符:对象赋值时调用,需处理自赋值和旧资源释放。
- 析构函数:对象生命周期结束时自动释放资源,避免内存泄漏。
- 动态分配:需要
delete
或delete[]
手动释放内存。 - 临时对象:生命周期短,尽量优化避免不必要的生成。