C++98中类的默认成员函数有:默认构造函数、析构函数、拷贝构造函数,拷贝赋值,取地址重载
C++11新增了移动构造和移动赋值函数
1. 构造函数
可以参考作者早期发布的文章:
2. 析构函数
功能:清理释放对象中资源
特点:
- 函数名:
~类名 - 无参数和返回值
- 未显式定义,会生成默认析构函数(内置类型:不处理;自定类型:另外调用
- 生命周期结束时自动调用
- 如果
申请了资源,一定要自己显式写析构函数(否则会内存泄漏 - 析构顺序:
后定义先析构(类似栈
使用示例:
cpp
class MyBuffer {
public:
MyBuffer()
{
this->data = new int[10];
std::cout << "MyBuffer()" << std::endl;
}
~MyBuffer()
{
delete[] this->data; //显式实现资源释放
std::cout << "~MyBuffer()" << std::endl;
}
private:
int* data;
};
3. 拷贝构造
功能:根据一个已存在对象构造一个相同的拷贝对象
特点:
- 构造函数的
重载 - 第一个参数是类类型对象
引用(传值调用拷贝构造,触发无穷递归调用 - 多参数:第一个外的参数必须有
缺省值 - 自定类型对象进行拷贝时必须调用拷贝构造::即
传参/传值都会触发 - 如果
申请了资源,一定要显式写拷贝构造函数(内置类型:默认浅拷贝,析构时会多次delete同一份资源;自定类型:另外调用
cpp
// 拷贝构造函数:深拷贝
MyBuffer(const MyBuffer& other)
{
this->data = new int[10]; //深拷贝
for (int i = 0; i < 10; ++i)
{
this->data[i] = other.data[i];
}
}
结论:自定类型尽量传引用传参(传值触发拷贝构造->深拷贝->浪费内存
4. 运算符重载
功能:支持对象像普通变量一样使用各种运算符
特点 / 注意:
- 不能重载:
.*::sizeof?:.和语法中没有的符号(eg:@ - 函数名:
operator运算符(参数数量和返回值类型由运算符本身特性决定 - 成员函数:第一个对象默认为
this指针,不需要显式传(少一个参数 - 前置 / 后置运算符:后置运算符参数增加
int形参,方便区分 - 重载
>>和<<:必须为全局函数(第一个形参为ostream / istream
4.1 赋值重载
功能:支持已存在对象像普通变量一样赋值(区分拷贝构造
特点:
- 参数:
const+引用,减少传值拷贝 - 返回值:类类型
引用(减少拷贝 / 支持连续赋值 - 如果
申请了资源,一定要显式写赋值重载(默认生成:内置类型浅拷贝;自定类型另外调用 - 赋值过程:检查自赋值->释放旧资源->申请新资源->拷贝
自赋值:释放自己的资源+重新访问拷贝,导致访问野指针- 现代写法:
传值传参,额外实现swap函数交换指针指向资源(不需要检查自赋值
使用示例:
cpp
MyBuffer& operator=(const MyBuffer& other)
{
if (this != &other) // 1. 检查自赋值
{
delete[] this->data; // 2. 释放当前对象的旧资源
this->data = new int[10]; // 3. 申请新的资源
for (int i = 0; i < 10; ++i)
{
this->data[i] = other.data[i]; // 4. 拷贝数据
}
}
return *this;
}
现代写法:
cpp
void swap(...){//...}
MyBuffer& operator=(MyBuffer other) //传值触发拷贝构造
{
swap(*this, other); //交换指针指向对象
return *this;
}
4.2 取地址运算符重载
特点:
- 包括普通重载和const重载
- 编译器自动生成的已经足够使用