类的 6 个默认成员函数
空类
类中没有成员函数和成员变量。
默认成员函数
在 C++ 里,当你定义一个类时,即便你没有明确编写某些成员函数,编译器也会自动为这个类生成 6 个默认的成员函数。用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
构造函数
定义
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。
特性
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
特征
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。构造函数权限是公有的可以自动调用,如果改成私有的无法自动调用。
- 构造函数可以重载。
#include <iostream>
using namespace std;
typedef int DataType;
class Stack {
public:
// 构造函数等价于 InitStack(int capacity = 4)
Stack(int capacity = 4) {
cout << "Stack(int capacity = 4)" << endl;
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = capacity;
_top = 0;
}
void Init() {
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = 4;
_top = 0;
}
void Push(int x) {
_a[_top++] = x;
}
void Destory() {
free(_a);
_a = nullptr;
_top = _capacity;
}
int Top() {
return _a[_top - 1];
}
private:
int* _a;
int _top;
int _capacity;
};
int main() {
Stack s;
// 构造函数等价于 Init
// s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
s.Destory();
return 0;
}
上面的代码可以成功运行,相比于之前的代码,构造函数 Stack 通过构造函数整合初始化逻辑,实例化对象时编译器自动调用构造函数完成初始化,无需手动调用 Init,简化流程且让对象初始化更自然高效。
没有定义构造函数
如果类中没有显式定义构造函数,则 C++ 编译器 会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。C++ 标准没有规定要初始化为 0,但有的编译器规定初始化为 0。
默认构造函数是指编译器生成的、全缺省的、无参的。
C++ 主要有内置类型 / 基本类型、语言本身定义的基础类型 int/char/double/指针等等和自定义、用 struct / class 等等定义的类型。
如果不写构造函数,编译器默认生成构造函数,内置类型可能不做处理,自定义类型会去调用它的默认构造函数。
#include <iostream>
using namespace std;
typedef int DataType;
class Stack {
public:
// 构造函数支持重载
Stack(DataType* a, int n) {
cout << "Stack(DataType* a, int n)" << endl;
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
memcpy(_a, a, sizeof(DataType) * n);
_capacity = n;
_top = 0;
}
// 构造函数等价于 Init
Stack(int capacity = 4) {
cout << "Stack(int capacity = 4)" << endl;
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = capacity;
_top = 0;
}
void Init() {
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = 4;
_top = 0;
}
void Push(int x) {
_a[_top++] = x;
}
// 析构函数在对象生命周期结束后自动调用
~Stack() {
cout << "~Stack()" << endl;
if (_a) {
free(_a);
_a = nullptr;
_top = 0;
_capacity = 0;
}
}
void Destory() {
free(_a);
_a = nullptr;
_top = _capacity;
}
int Top() {
return _a[_top - 1];
}
private:
int* _a;
int _top;
int _capacity;
};
class Date {
public:
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 内置类型
int _year;
int _month;
int _day;
// 自定义类型
Stack _st;
};
int main() {
Date d1;
Date d2;
d1.Print();
return 0;
}
有自定义类型 内置类型会被初始化为 0,编译器会自动调用自定义类型的默认构造函数。
没有自定义类型 内置类型不会被初始化为 0。
总结
- 一般情况下,有内置类型成员,就需要自己写构造函数,不能用编译器生成的。
- 全部都是自定义类型,可以考虑让编译器自己生成。
- 永远显式初始化内置类型成员。
class Date {
public:
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 这里不是初始化,因为这里只是声明
// 这里是缺省值,给编译器生成的默认构造函数用
int _year = 1;
int _month = 1;
int _day = 1;
Stack _st;
};
内置类型构造函数的调用与初始化
无参构造函数
#include <iostream>
using namespace std;
class Date {
public:
// 无参构造函数
Date() {
cout << "Date()" << endl;
_year = 1;
_month = 2;
_day = 1;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 内置类型
// 这里不是初始化,因为这里只是声明
// 这里是缺省值,给编译器生成的默认构造函数用
// 无参构造函数这里可以添加缺省值
int _year = 1;
int _month = 1;
int _day = 1;
};
int main() {
Date d1;
d1.Print();
// 对象。函数()
return 0;
}
无参构造函数可以在内置类型的声明后面添加缺省值,缺省值可以给编译器默认生成的构造函数使用,在 main 函数里面,d1 是对象,和普通函数不一样的是,这里不能加括号,如果添加括号会报错。如果声明和无参构造函数后面同时添加值,则使用无参构造函数的值。
有参构造函数
#include <iostream>
using namespace std;
class Date {
public:
// 有参构造函数
Date(int year, int month, int day) {
cout << "Date(int year, int month, int day)" << endl;
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 有参构造函数这里不能添加缺省值
int _year;
int _month;
int _day;
};
int main() {
Date d2(2023, 1, 2);
d2.Print();
return 0;
}
```\n
与无参构造函数不一样的是有参构造函数不可以再内置类型的声明后面添加缺省值,**在 main 函数里面,d2 是对象,对象后面添加括号,对内置函数进行初始化。**
```cpp
#include <iostream>
using namespace std;
class Date {
public:
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
// 内置类型
// 这里不是初始化,因为这里只是声明
// 这里是缺省值,给编译器生成的默认构造函数用
int _year = 1;
int _month = 1;
int _day = 1;
};
int main() {
Date d1;
d1.Print();
return 0;
}
如果不写构造函数,C++ 编译器默认生成无参构造函数,即不能在对象后面添加括号进行传参,否则会报错。
如果在内置类型的声明后面不写缺省值,则内置类型是随机值。
注意
- 如果一旦显式定义任何构造函数,编译器将不再生成构造函数。
- 无参构造函数和全缺省构造函数只能存在一个。因为缺省函数可以不写参数与无参构造函数调用不明确。
- 一般情况下,构造函数都需要我们自己写。内置类型的成员都有缺省值且初始化符合要求 和全是自定义类型的构造且这些类型都定义默认值这两种情况不需要写构造函数,其余情况都要写构造函数。
析构函数
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
特征
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。
- 对象生命周期结束时,C++ 编译系统系统自动调用析构函数。
- 析构函数没有参数,所以它不支持重载。
- 内置类型不做处理,自定义类型会去调用它的析构函数。
- 一般情况下,有动态申请资源,就需要显示写析构函数释放资源。
- 没有动态申请资源,不需要写析构。
- 需要释放资源的成员都是自定义类型,不需要写析构。
#include <iostream>
using namespace std;
typedef int DataType;
class Stack {
public:
// 构造函数等价于 Init
Stack(int capacity = 4) {
cout << "Stack(int capacity = 4)" << endl;
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = capacity;
_top = 0;
}
void Init() {
_a = (DataType*)malloc(sizeof(DataType) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_capacity = 4;
_top = 0;
}
void Push(int x) {
_a[_top++] = x;
}
// 析构函数等价于 Destory
~Stack() {
cout << "~Stack()" << endl;
if (_a) {
free(_a);
_a = nullptr;
_top = 0;
_capacity = 0;
}
}
void Destory() {
free(_a);
_a = nullptr;
_top = _capacity;
}
int Top() {
return _a[_top - 1];
}
private:
int* _a;
int _top;
int _capacity;
};
int main() {
Stack s;
// 构造函数等价于 Init
// s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
// s.Destory();
return 0;
}
有了构造函数和析构函数,就不害怕没写初始化和清理函数了,也简化了代码。
拷贝构造函数
只有单个形参,该形参是对本类类型对象的引用 (一般常用 const 修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。拷贝构造使用 1 个对象去初始化另一个对象,是已经存在的两个对象之间的拷贝。
特征
- 拷贝构造函数是构造函数的一个重载形式。
- 自定义类型必须要用拷贝构造去完成,内置类型直接拷贝,不需要拷贝构造函数。
- 任何类型的指针都是内置类型。
一般建议在引用前加上 const,防止代码出现错误。
错误代码
正确代码
如果加上 const 则代码会报错,有利于检查代码
拷贝构造函数的参数只有一个且必须是类类型对象的引用或指针,使用传值方式编译器直接报错,因为会引发无穷递归调用。
引用
指针
注意 如果使用指针传参时不写取地址符,则函数传参不会进入拷贝构造函数,代码可能会报错。
#include <iostream>
using namespace std;
class Date {
public:
Date(int year = 1900, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数
Date(Date& d) // 类对象的引用
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
void Func(Date d) {}
void Func(int d) {}
int main() {
Date d1(2023, 4, 25);
// Date d2(d1); // 规定了自定义类型必须要用拷贝构造去完成
Func(d1); // 内置类型直接拷贝
Func(10);
return 0;
}
C++ 规定,自定义类型必须使用拷贝构造函数才能完成,内置类型可以直接拷贝。如果传参传的是构造函数,则函数传参后先进入拷贝构造函数,然后进入函数体。
例如在上面的代码中,Func(d1) 传参之后先进入拷贝构造函数,再进入 Func 函数。
Date d2(d1) 在这句代码,d1 就是 d 的别名,this 指针指向的是 d2。
在上面代码中,_year、_month 和 _day 是私有的,只能在类里面访问,在外面不能访问,拷贝构造函数中,等号左右两边的变量不是声明中的 _year、_month 和 _day,等号左边的 _year、_month 和 _day 是 this 指针的,等号右边的 _year、_month 和 _day 是 d 的。可以将拷贝构造函数改写成这样。
- 若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
没有开辟空间
#include <iostream>
using namespace std;
class Date {
public:
Date(int year = 1900, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2023, 4, 25);
Date d2(d1);
return 0;
}
由结果可知,如果没有开辟空间,默认的拷贝构造函数可以使用,不需要写拷贝构造函数。
开辟空间
#include <iostream>
using namespace std;
class Stack {
public:
Stack() {
cout << "Stack()" << endl;
_a = (int*)malloc(sizeof(int) * 4);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_top = 0;
_capacity = 4;
}
Stack(int capacity) {
cout << "Stack(int capacity)" << endl;
_a = (int*)malloc(sizeof(int) * capacity);
if (nullptr == _a) {
perror("malloc fail");
return;
}
_top = 0;
_capacity = capacity;
}
~Stack() {
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_capacity = _top = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main() {
Stack st1;
Stack st2(st1);
return 0;
}
上面的程序会崩溃,原因是拷贝构造函数在拷贝空间时,会将 st1 和 st2 指向同一块空间,析构函数会先释放 st2,此时空间销毁,再去释放 st1 的空间程序会崩溃。如果 1 个修改,因为指向同一个空间另一个也会被修改。
释放空间的顺序为先释放 st2 再释放 st1,拷贝构造函数是将 st1 拷贝给 st2,先有 st1 再有 st2,符合先进后出原则。
深拷贝
Stack(const Stack& st) {
_a = (int*)malloc(sizeof(int) * st._capacity);
if (nullptr == _a) {
perror("malloc fail");
return;
}
memcpy(_a, st._a, sizeof(int) * st._top);
_top = st._top;
_capacity = st._capacity;
}
运算符重载
定义
运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。是否要重载运算符,要明确这个运算符对这个类是否有意义,以日期函数为例,日期 + 日期没有意义,日期 - 日期有意义。
函数名字
关键字 operator 后面接需要重载的运算符符号。
函数原型
返回值类型 operator 操作符 (参数列表)
返回类型 operator 运算符 (参数列表) {
// 函数体
}
规则
1. 不能通过连接其他符号来创建新的操作符:比如 operator@ 2. 重载操作符必须有一个类类型参数,因为要对自定义类型进行控制,不能全部的参数都是内置类型,例如:bool operator<(const int& x, const int& y) 3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型 +,不能改变其含义 4. 作为类成员函数重载时,其形参看起来比操作数数目少 1,因为成员函数的第一个参数为隐藏的 this。 5. .*、::、sizeof、?:、. 注意以上 5 个运算符不能重载。这个经常在选择题中出现。 6. 在全局函数中,运算符是几个操作数,重载函数就有几个参数。在成员函数中,重载函数少一个参数。
定义方式
运算符重载函数的定义方式有两种,既可以作为类的成员函数,也能作为全局函数。
这里以日期函数为例进行解释运算符重载。
全局函数
#include <iostream>
using namespace std;
class Date {
public:
Date(int year = 1900, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
bool operator<(const Date& x1, const Date& x2) {
if (x1._year < x2._year) {
return true;
} else if (x1._year == x2._year && x1._month < x2._month) {
return true;
} else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day) {
return true;
}
return false;
}
int main() {
Date d1(2023, 4, 25);
Date d2(2023, 5, 25);
cout << (d1 < d2) << endl;
cout << (operator<(d1, d2)) << endl;
return 0;
}
内置类型可以直接比较,自定义类型不能直接比较,<< 是流插入运算符,它的优先级高于比较运算符,所以流插入运算符和比较运算符同时存在时应给比较运算符添加括号。
在上面的代码中,d1 < d2; 和 operator<(d1, d2); 是完全等价的,因为运算符重载本质上是函数调用,d1 < d2 是隐式调用,operator<(d1, d2); 是显示函数调用,编译器会将 d1 < d2 解释为对 operator< 函数的调用,就像调用普通函数一样。
如果要在全局函数使用类中的变量,权限必须是 public,即权限是公有的,如果权限是私有的,则不能在类外面访问变量。
成员函数
#include <iostream>
using namespace std;
class Date {
public:
// 构造函数
Date(int year = 1, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
// 作为类成员函数重载时,其形参看起来比操作数数目少 1,因为成员函数的第一个参数为隐藏的 this
bool operator<(const Date& x) {
if (_year < x._year) // 这里等价于 if (this->_year < x._year)
return true;
else if (_year == x._year && _month < x._month)
return true;
else if (_year == x._year && _month == x._month && _day < x._day)
return true;
return false;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2018, 9, 26);
Date d2(2018, 10, 27);
// 日期 - 日期有意义
// 日期 + 日期没意义
// 是否要重载运算符,这个运算符对这个类是否有意义
// 全局函数
// d1 < d2;
// 转换成 operator<(d1, d2);
// 成员函数
d1 < d2;
// 转换成 d1.operator<(d2);
d1.operator<(d2);
if (d1 < d2) {}
return 0;
}
相比于全局函数,如果是成员函数,函数调用应该写成 d1.operator<(d2); 和全局函数类似,d1.operator<(d2); 和 d1 < d2 是等价的,如下图,在汇编语言中这两段代码的地址一样,编译器会将 d1 < d2 转换成 d1.operator<(d2); 这句代码。
赋值运算符重载
赋值运算符重载与拷贝构造之间的区别
赋值运算符是一种拷贝,拷贝构造是使用 1 个对象去初始化另一个对象,赋值运算符重载是已经存在的两个对象之间的拷贝。
规则
- 参数类型:const T&,传递引用可以提高传参效率。
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。
- 检测是否自己给自己赋值。
- *返回 this:要复合连续赋值的含义。
#include <iostream>
using namespace std;
class Date {
public:
// 构造函数
Date(int year = 1, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
// 作为类成员函数重载时,其形参看起来比操作数数目少 1,因为成员函数的第一个参数为隐藏的 this
bool operator<(const Date& x) {
if (this->_year < x._year) // 这里等价于 if (this->_year < x._year)
return true;
else if (this->_year == x._year && this->_month < x._month)
return true;
else if (this->_year == x._year && this->_month == x._month && this->_day < x._day)
return true;
return false;
}
void operator=(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2018, 9, 26);
Date d2(2018, 10, 27);
d1 = d2;
d1.operator=(d2);
return 0;
}
这里是浅拷贝,也就是值拷贝没有开辟空间。和运算符重载类似 d1 = d2; 等价于 d1.operator=(d2);
如果要进行连续赋值则需要返回 this 指针,返回不能是 void。在连续赋值中,运算是从右向左执行的,即先执行 k = 0,返回 k 的值,以此类推,j = k 返回 j 的值,最后 i = j 返回 i 的值。
int i, j, k;
i = j = k = 0;
如果是连续赋值,d2 = d3 返回的是 d2,在这里 this 是 d2 的地址,即返回 this 指针。this 指针不能在形参和实参的位置显示,但在函数内部使用。类似的 d1 = d2 返回 d1,this 此时是 d1 的地址。
Date d1(2018, 9, 26);
Date d2(2018, 10, 27);
Date d3(2018, 11, 28);
d1 = d2 = d3;
Date operator=(const Date& d) {
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
return *this;
}
在这里如果添加拷贝构造函数,在连续赋值中,会调用两次拷贝构造函数,可以使用引用返回提高代码效率。在这里 this 指针是全局变量,出了函数作用域不会被销毁。
拷贝构造函数
Date(const Date& d) {
cout << "Date(const Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
原来的
Date operator=(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
改进后
Date& operator=(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
函数返回时会生成临时副本返回,连续赋值会生成临时副本,调用两次拷贝构造函数,而使用引用返回不会调用拷贝构造函数,而是直接返回对象本身的别名。
- 赋值运算符重载不可以使用全局变量,它是默认成员函数,如果写会与默认生成的冲突。类似的,拷贝构造函数、构造函数、析构函数都不能使用全局变量,即不能写在类外面,但是可以声明和定义分离,运算符重载函数不是默认成员函数,但是建议写在类里面,因为受访问限定符的限制。
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。默认生成的赋值运算符重载函数支持连续赋值。
注意 内置类型成员变量是直接赋值的,也就是值拷贝或者浅拷贝,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
如果开辟空间需要写赋值运算符重载函数,不开辟空间不需要写。
没有写赋值运算符重载函数
前置 ++ 和后置 ++
前置 ++ 是先 ++ 再使用,返回的是 +1 之后的结果,可以直接使用 this 指针加 1。 后置 ++ 是先使用后 ++,返回的是 +1 之前的结果,因此要使用临时变量,再 +1。 为了让前置 ++ 和后置 ++ 两个函数构成重载,在后置 ++ 的参数位置添加整型 int 用来占位,来使两个函数构成函数重载。
#include <iostream>
using namespace std;
class Date {
public:
// 构造函数
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
// 前置 ++
Date& operator++() {
*this += 1;
return *this;
}
// 后置 ++
// int 是为了占位,不是为了接受具体的值,方便区分,为了跟前置 ++ 构成重载
Date operator++(int) {
Date tmp = *this;
*this += 1;
return tmp;
}
private:
int _year;
int _month;
int _day;
};
void TestDate() {
// 前置 ++
Date d3(2025, 7, 13);
++d3;
d3.Print();
// 后置 ++
Date d4(2025, 7, 13);
d4++;
d4.Print();
}
int main() {
TestDate();
return 0;
}
注意 在前置 ++ 中使用传引用返回,在后置 ++ 中不能使用传引用返回,是因为 tmp 是局部变量,局部变量在函数出了作用域后会销毁,无法使用传引用返回。
前置 -- 和后置 --
前置 -- 是先 -- 再使用,返回的是 -1 之后的结果,可以直接使用 this 指针减 1。 后置 -- 是先使用后 --,返回的是 -1 之前的结果,因此要使用临时变量,再 -1。 和 ++ 类似,为了让前置 -- 和后置 -- 两个函数构成重载,在后置 -- 的参数位置添加整型 int 用来占位,来使两个函数构成函数重载。
#include <iostream>
using namespace std;
class Date {
public:
// 构造函数
Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
// 前置 --
Date& operator--() {
*this -= 1;
return *this;
}
// 后置 --
// int 是为了占位,不是为了接受具体的值,方便区分,为了跟前置 -- 构成重载
Date operator--(int) {
Date tmp = *this;
*this -= 1;
return tmp;
}
private:
int _year;
int _month;
int _day;
};
void TestDate() {
// 前置 --
Date d3(2025, 7, 13);
--d3;
d3.Print();
// 后置 --
Date d4(2025, 7, 13);
d4--;
d4.Print();
}
int main() {
TestDate();
return 0;
}
总结
类的默认成员函数与运算符重载,是 C++ 管理对象生命周期和简化操作的核心工具。它们规范了对象从创建到销毁、从拷贝到运算的全过程,尤其在资源处理时,深拷贝、析构函数等的合理实现直接影响程序稳定性。理解这些机制,能帮助我们写出更规范、高效的面向对象代码,充分发挥 C++ 的封装与抽象优势。


