【本节目标】
- 类的 6 个默认成员函数
- 构造函数
- 析构函数
- 拷贝构造函数
- 赋值运算符重载
- const 成员函数
- 取地址及 const 取地址操作符重载
1. 类的 6 个默认成员函数
空类(无显式成员)并非真的为空,编译器会自动生成 6 个默认成员函数 ------ 用户未显式实现时,编译器自动补充的成员函数。
| 默认成员函数 | 核心作用 |
|---|---|
| 构造函数 | 对象创建时初始化(保证成员有合适初始值) |
| 析构函数 | 对象销毁时清理资源(如堆内存、文件句柄) |
| 拷贝构造函数 | 用已存在对象初始化新对象(同类对象拷贝创建) |
| 赋值运算符重载 | 将一个对象的值赋给另一个已存在的同类对象 |
| 取地址运算符重载 | 获取普通对象的地址(默认生成,无需手动实现) |
| const 取地址运算符重载 | 获取 const 对象的地址(默认生成,无需手动实现) |
!note\] 关键特性若用户显式实现了某类默认成员函数,编译器将不再生成该函数(部分例外如析构函数与拷贝构造的组合,后续详解)。
2. 构造函数
2.1 概念
创建对象时,编译器自动调用的特殊成员函数,核心任务是初始化对象 (而非开空间创建对象),避免每次创建对象后手动调用初始化方法(如Init)。
入门示例(问题场景)
cpp
class Date
{
public:
// 手动初始化方法,每次创建对象需调用
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print() { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(2022, 7, 5); // 手动调用初始化,繁琐
d1.Print();
return 0;
}
构造函数解决示例
cpp
class Date
{
public:
// 构造函数:名字与类名相同,无返回值
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print() { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 7, 5); // 创建对象时自动调用构造函数,无需手动初始化
d1.Print(); // 输出:2022-7-5
return 0;
}
2.2 核心特性
- 函数名与类名完全相同 ,无返回值(无需写
void,直接定义); - 对象实例化时编译器自动调用,且整个对象生命周期仅调用一次;
- 支持重载(可定义多个参数不同的构造函数);
cpp
class Date
{
public:
// 无参构造函数
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
// 带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
// 全缺省构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
```
4. **无参构造函数的调用陷阱**:创建无参对象时,对象后不能加括号(否则视为函数声明);
```cpp
void TestDate()
{
Date d1; // 正确:调用无参构造函数
// Date d3(); // 错误:声明一个返回Date类型的无参函数,非对象创建
}
```
5. **默认构造函数的唯一性**:无参构造、全缺省构造、编译器默认生成的构造函数,统称 "默认构造函数",一个类只能有一个(否则编译冲突);
```cpp
class Date
{
public:
Date() {} // 无参构造
Date(int year = 1900, int month = 1, int day = 1) {} // 全缺省构造
};
// Date d1; // 编译报错:默认构造函数不唯一
```
6. **编译器自动生成规则**:
- 若用户未显式定义任何构造函数,编译器生成无参默认构造函数;
- 若用户显式定义了任意构造函数(无论带参 / 无参),编译器不再生成默认构造函数;
```cpp
class Date
{
public:
// 显式定义带参构造函数,编译器不再生成无参构造
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
// Date d1; // 编译报错:无合适的默认构造函数可用
```
7. **默认构造函数的初始化行为**:
- 对**内置类型**(int、char 等):不初始化,成员值为随机值;
<font color="#ff0000"> - 对<font color="#00b050">自定义类型</font>(class/struct 定义的类型):调用其默认构造函数初始化; </font>
```cpp
class Time // 自定义类型
{
public:
Time() // Time的默认构造函数
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 内置类型:默认构造不初始化,值为随机
int _year;
int _month;
int _day;
// 自定义类型:默认构造调用Time的默认构造
Time _t;
};
int main()
{
Date d; // 输出:Time()(_t被初始化,_year/_month/_day为随机值)
return 0;
}
```
5. **C++11 补丁**:内置类型成员可在类中声明时指定默认值(编译器生成默认构造时会使用该值初始化);
```cpp
class Date
{
private:
// 内置类型声明时指定默认值
int _year = 1970;
int _month = 1;
int _day = 1;
Time _t; // 自定义类型仍调用其默认构造
};
int main()
{
Date d; // _year =1970, _month=1, _day=1,_t初始化正常
return 0;
}
```
> [!tip] 实践建议尽量显式定义构造函数(无参 / 全缺省),避免依赖编译器生成的默认构造(内置类型初始化不可控)。
## 3. 析构函数
### 3.1 概念
与构造函数功能相反,对象生命周期结束时(如局部对象出作用域)编译器自动调用,核心任务是**清理对象申请的资源**(如堆内存、文件流),而非销毁对象本身(对象销毁由编译器完成)。
#### 入门示例(资源泄漏场景)
```cpp
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
// 申请堆内存资源
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data) { _array[_size++] = data; }
// 无析构函数:对象销毁时堆内存未释放,导致资源泄漏
private:
DataType* _array; // 堆内存资源
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
// s销毁时,_array指向的堆内存未释放,资源泄漏
}
析构函数解决示例
cpp
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data) { _array[_size++] = data; }
// 析构函数:清理堆内存资源
~Stack()
{
if (_array)
{
free(_array); // 释放堆内存
_array = nullptr; // 避免野指针
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
// s销毁时自动调用~Stack(),堆内存释放,无资源泄漏
}
3.2 核心特性
-
函数名 :类名前加
~(如~Date()),无参数、无返回值; -
不可重载:一个类只能有一个析构函数(显式定义或编译器生成);
-
自动调用时机:对象生命周期结束时触发(局部对象出作用域、全局对象程序结束时);
-
编译器自动生成规则:
- 若用户未显式定义析构函数,编译器生成默认析构函数;
- 默认析构函数对内置类型:不做任何清理(如堆内存不会释放);
- 默认析构函数对自定义类型:调用其析构函数清理;
cpp
class Time
{
public:
~Time() // Time的析构函数
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
int _year = 1970; // 内置类型:默认析构不清理
Time _t; // 自定义类型:默认析构调用~Time()
};
int main()
{
Date d; // 程序结束时输出:~Time()(_t被清理,_year无操作)
return 0;
}
```
5. **显式定义的必要性**:
- 类中**无资源申请**(如 Date 类,仅内置类型成员):无需显式定义,用编译器默认析构即可;
- 类中**有资源申请**(如 Stack 类,堆内存、文件句柄):必须显式定义析构函数,否则导致资源泄漏。
> [!warning] 常见错误忘记在析构函数中释放堆内存,或释放后未将指针置为`nullptr`,可能导致野指针访问。
## 4. 拷贝构造函数
### 4.1 概念
用**已存在的同类对象**创建新对象时,编译器自动调用的特殊成员函数(本质是构造函数的重载形式),核心任务是复制已有对象的成员值。
#### 生活类比
双胞胎:用 "已存在的人"(哥哥)复制出 "新的人"(弟弟),二者初始状态完全一致。
#### 入门示例
```cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 拷贝构造函数:用已有对象d初始化新对象
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print() { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 7, 5); // 调用普通构造函数
Date d2(d1); // 调用拷贝构造函数,d2复制d1的状态
d2.Print(); // 输出:2022-7-5(与d1一致)
return 0;
}
4.2 核心特性
-
函数原型 :参数必须是 "本类类型的 const 引用"(
const 类名&);- 若用传值方式(
Date d),会引发无穷递归(传值时需调用拷贝构造,拷贝构造又需传值,循环往复);
- 若用传值方式(
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1) {}
// 正确:const引用参数
Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; }
// 错误:传值参数,引发无穷递归
// Date(Date d) { _year = d._year; _month = d._month; _day = d._day; }
private:
int _year;
int _month;
int _day;
};
```
1. **编译器自动生成规则**:
- 若用户未显式定义拷贝构造,编译器生成默认拷贝构造;
- 默认拷贝构造的行为:**浅拷贝(值拷贝)** ------ 按内存字节序逐字节复制;
- 内置类型:直接复制字节;
- 自定义类型:调用其拷贝构造函数复制;
```cpp
class Time
{
public:
Time(int hour = 1, int minute = 1, int second = 1) {}
// Time的拷贝构造
Time(const Time& t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time&)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
int _year = 1970; // 内置类型:浅拷贝直接复制
Time _t; // 自定义类型:调用Time的拷贝构造
};
int main()
{
Date d1;
Date d2(d1); // 输出:Time::Time(const Time&)(默认拷贝构造触发)
return 0;
}
```
2. **浅拷贝的问题与显式定义的必要性**:
- 无资源申请的类(如 Date):浅拷贝足够,无需显式定义;
- 有资源申请的类(如 Stack):浅拷贝会导致**双重释放**(两个对象指向同一块堆内存,销毁时两次调用`free`);
```cpp
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array) { perror("malloc失败"); return; }
_size = 0;
_capacity = capacity;
}
~Stack()
{
if (_array) { free(_array); _array = nullptr; } // 释放堆内存
}
// 未显式定义拷贝构造,使用编译器默认浅拷贝
private:
DataType* _array; // 堆内存资源
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1;
s1.Push(1);
Stack s2(s1); // 浅拷贝:s2._array = s1._array(指向同一块堆内存)
return 0;
// s1和s2销毁时,均调用~Stack(),双重free导致程序崩溃
}
```
> [!solution] 解决方案涉及资源申请的类,必须显式定义拷贝构造函数,实现**深拷贝**(为新对象重新申请资源,复制内容而非地址)。
3. **典型调用场景**:
- 用已存在对象创建新对象(`Date d2(d1)`);
- 函数参数为类类型对象(传值时触发拷贝构造);
- 函数返回值为类类型对象(返回时触发拷贝构造,部分编译器可能优化);
```cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{ cout << "Date(int,int,int):" << this << endl; }
Date(const Date& d)
{ cout << "Date(const Date& d):" << this << endl; }
~Date() { cout << "~Date():" << this << endl; }
private:
int _year;
int _month;
int _day;
};
Date Test(Date d) // 传参:调用拷贝构造生成d
{
Date temp(d); // 用d创建temp:调用拷贝构造
return temp; // 返回temp:调用拷贝构造生成临时对象
}
int main()
{
Date d1(2022, 1, 13); // 调用普通构造
Test(d1); // 触发多次拷贝构造(视编译器优化而定)
return 0;
}
```
> [!tip] 性能优化建议函数参数尽量用**const 引用**(避免拷贝构造开销),返回值优先用引用(无拷贝开销),仅当返回局部对象时用值返回。
> [!tip] 浅拷贝问题:浅拷贝会让多个对象共享同一份资源,而析构时却都以为自己拥有它,从而产生析构两次的问题。
> [!tip] 需要动态开辟空间的需要深拷贝
## 5. 赋值运算符重载
### 5.1 运算符重载基础
C++ 允许为类重载运算符(如`==`、`+`、`=`),本质是 "具有特殊函数名的函数",目的是增强代码可读性(用运算符表达对象操作,更直观)。
#### 核心规则
- 函数名:`operator + 运算符`(如`operator==`、`operator=`);
- 必须有**类类型参数**(否则与内置类型运算符冲突);
- 内置类型运算符含义不能改变(如不能重载`int`的`+`为减法);
- 5 个运算符不能重载:`.*`、`::`、`sizeof`、`?:`、`.`;
- 作为类成员函数重载时,第一个参数为隐含的`this`指针(指向左操作数),形参个数比运算符操作数少 1;
#### 示例:重载`==`运算符
```cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 成员函数重载==:左操作数为this,右操作数为d2
bool operator==(const Date& d2)
{
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 7, 5), d2(2022, 7, 6);
cout << (d1 == d2) << endl; // 等价于d1.operator==(d2),输出0
return 0;
}
5.2 赋值运算符重载(重点)
赋值运算符(=)是特殊的运算符重载,核心用于 "将一个已存在对象的值赋给另一个已存在对象"(与拷贝构造的 "用已有对象创建新对象" 区分)。
1. 正确格式(必须遵守)
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 赋值运算符重载格式
Date& operator=(const Date& d)
{
// 1. 检测自赋值(避免自己给自己赋值时的不必要操作)
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 2. 返回*this(支持连续赋值,如d1 = d2 = d3)
return *this;
}
private:
int _year;
int _month;
int _day;
};
格式说明
- 参数类型 :
const Date&------ 传引用避免拷贝开销,const防止修改右操作数; - 返回值类型 :
Date&------ 返回引用避免拷贝开销,且支持连续赋值; - 自赋值检测 :
this != &d------ 若d1 = d1(自赋值),直接返回,避免无效操作(尤其涉及资源时,防止双重释放); - 返回 * this :使赋值表达式返回左操作数,支持
d1 = d2 = d3(等价于d1.operator=(d2.operator=(d3)))。
2. 必须重载为类成员函数
赋值运算符不能重载为全局函数,否则与编译器生成的默认赋值运算符冲突:
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1) {}
int _year;
int _month;
int _day;
};
// 错误:全局赋值运算符重载与默认生成的冲突
Date& operator=(Date& left, const Date& right)
{
if (&left != &right)
{
left._year = right._year;
left._month = right._month;
left._day = right._day;
}
return left;
}
// 编译报错:error C2801: "operator ="必须是非静态成员
3. 编译器自动生成规则
- 若用户未显式定义,编译器生成默认赋值运算符重载;
- 默认行为:浅拷贝(值拷贝) ------ 内置类型直接赋值,自定义类型调用其赋值运算符重载;
- 显式定义的必要性:与拷贝构造一致,涉及资源申请的类必须显式实现(深拷贝),否则导致双重释放;
cpp
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 10)
{
_array = (DataType*)malloc(capacity * sizeof(DataType));
if (nullptr == _array) { perror("malloc失败"); return; }
_size = 0;
_capacity = capacity;
}
~Stack()
{
if (_array) { free(_array); _array = nullptr; }
}
// 未显式定义赋值运算符,使用默认浅拷贝
private:
DataType* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack s1, s2;
s1.Push(1);
s2 = s1; // 浅拷贝:s2._array = s1._array(同一块堆内存)
return 0;
// s1和s2销毁时双重free,程序崩溃
}
5.3 前置 ++ 与后置 ++ 重载(扩展)
++运算符有前置(++d)和后置(d++)两种形式,需通过重载区分:
- 前置 ++:先自增,返回自增后的对象(引用返回,无拷贝);
- 后置 ++:先返回原对象,再自增(值返回,需拷贝临时对象);
- 区分技巧:后置 ++ 重载时多增加一个
int类型的占位参数(编译器自动传递,用户无需显式传参)。
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 前置++:返回引用(自增后的对象)
Date& operator++()
{
_day += 1;
return *this;
}
// 后置++:int为占位参数,返回值(自增前的临时对象)
Date operator++(int)
{
Date temp(*this); // 保存原状态
_day += 1; // 自增
return temp; // 返回原状态
}
void Print() { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2022, 1, 13);
Date d1 = ++d; // 前置++:d先自增为2022-1-14,d1复制该状态
d1.Print(); // 输出:2022-1-14
Date d2 = d++; // 后置++:d先复制给d2(2022-1-14),再自增为2022-1-15
d2.Print(); // 输出:2022-1-14
d.Print(); // 输出:2022-1-15
return 0;
}
6. 日期类完整实现(综合示例)
cpp
class Date
{
public:
// 获取某年某月的天数(静态成员函数:无this指针,可通过类名调用)
static int GetMonthDay(int year, int month)
{
static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int day = days[month];
// 闰年2月29天
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day += 1;
}
return day;
}
// 全缺省构造函数
Date(int year = 1900, int month = 1, int day = 1)
{
// 合法性检查
if (year >= 1 && month >= 1 && month <= 12 && day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "非法日期:" << year << "-" << month << "-" << day << endl;
}
}
// 拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// 赋值运算符重载
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
// 析构函数(无资源申请,无需显式实现,编译器生成即可)
~Date() {}
// 日期+=天数(修改原对象,引用返回)
Date& operator+=(int day)
{
if (day < 0)
{
return *this -= (-day); // 复用-=逻辑
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return *this;
}
// 日期+天数(不修改原对象,值返回)
Date operator+(int day)
{
Date temp(*this); // 拷贝原对象
temp += day; // 复用+=逻辑
return temp;
}
// 日期-=天数(修改原对象,引用返回)
Date& operator-=(int day)
{
if (day < 0)
{
return *this += (-day); // 复用+=逻辑
}
_day -= day;
while (_day < 1)
{
_month--;
if (_month < 1)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// 日期-天数(不修改原对象,值返回)
Date operator-(int day)
{
Date temp(*this);
temp -= day;
return temp;
}
// 前置++
Date& operator++() { return *this += 1; }
// 后置++
Date operator++(int)
{
Date temp(*this);
*this += 1;
return temp;
}
// 前置--
Date& operator--() { return *this -= 1; }
// 后置--
Date operator--(int)
{
Date temp(*this);
*this -= 1;
return temp;
}
// 关系运算符重载
bool operator==(const Date& d)
{
return _year == d._year && _month == d._month && _day == d._day;
}
bool operator!=(const Date& d) { return !(*this == d); }
bool operator>(const Date& d)
{
if (_year > d._year) return true;
else if (_year == d._year && _month > d._month) return true;
else if (_year == d._year && _month == d._month && _day > d._day) return true;
return false;
}
bool operator>=(const Date& d) { return *this > d || *this == d; }
bool operator<(const Date& d) { return !(*this >= d); }
bool operator<=(const Date& d) { return !(*this > d); }
// 日期-日期:返回天数差,最好的方法--最强王者。
int operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int dayCount = 0;
while (min != max)
{
++min;
++dayCount;
}
return dayCount * flag;
}
// 打印日期
void Print() { cout << _year << "-" << _month << "-" << _day << endl; }
private:
int _year;
int _month;
int _day;
};
7. const 成员函数
7.1 概念
用const修饰的类成员函数,本质是修饰该函数隐含的this指针,限制函数对类成员的修改(this指针类型变为const 类名* const)。
语法格式
cpp
class Date
{
public:
// const成员函数:const修饰this指针
void Print() const
{
// _year = 2023; // 编译报错:const成员函数不能修改成员变量
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
编译器处理逻辑
- 普通成员函数的
this指针:类名* const(指针指向不可改,指向的内容可改); - const 成员函数的
this指针:const 类名* const(指针指向不可改,指向的内容也不可改)。
7.2 调用规则(面试高频)
- const 对象只能调用 const 成员函数 (const 对象的
this是const 类名*,只能匹配 const 成员函数的this类型); - 非 const 对象可以调用任意成员函数 (非 const 对象的
this是类名*,可隐式转换为const 类名*); - const 成员函数不能调用非 const 成员函数 (非 const 成员函数的
this可修改成员,与 const 成员函数的限制冲突); - 非 const 成员函数可以调用 const 成员函数 (const 成员函数的
this是const 类名*,非 const 成员函数的this可兼容)。
示例验证
cpp
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// 非const成员函数
void Print()
{
cout << "Print()" << endl;
PrintConst(); // 规则4:非const可调用const成员函数
}
// const成员函数
void PrintConst() const
{
cout << "PrintConst()" << endl;
// Print(); // 规则3:const不能调用非const成员函数(编译报错)
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(2022, 1, 13); // 非const对象
d1.Print(); // 规则2:非const可调用非const成员函数
d1.PrintConst(); // 规则2:非const可调用const成员函数
const Date d2(2022, 1, 13); // const对象
// d2.Print(); // 规则1:const不能调用非const成员函数(编译报错)
d2.PrintConst(); // 规则1:const可调用const成员函数
}
实践建议若成员函数不修改任何成员变量,建议声明为 const 成员函数 ------ 既保证代码安全性,又支持 const 对象调用。
8. 取地址及 const 取地址操作符重载
这两个默认成员函数用于获取对象的地址,编译器会自动生成,无需手动实现,仅在特殊场景(如隐藏对象真实地址)下需显式重载。
8.1 默认生成的行为
cpp
class Date
{
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
const Date d2;
cout << &d1 << endl; // 调用默认的取地址重载(返回&d1)
cout << &d2 << endl; // 调用默认的const取地址重载(返回&d2)
return 0;
}
8.2 显式重载示例(特殊需求)
cpp
class Date
{
public:
// 取地址重载:返回普通对象地址
Date* operator&()
{
// 特殊需求:返回nullptr隐藏真实地址
return nullptr;
}
// const取地址重载:返回const对象地址
const Date* operator&() const
{
return nullptr;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
const Date d2;
cout << &d1 << endl; // 输出:00000000(返回nullptr)
cout << &d2 << endl; // 输出:00000000(返回nullptr)
return 0;
}
关键提示绝大多数场景下,无需显式重载这两个运算符,使用编译器生成的默认版本即可。