默认成员函数:⽤⼾没有显式实现,编译器⾃动⽣成的成员函数。
类中如果不写任何实现,编译器会默认生成6 个成员函数:
构造函数、析构函数、拷贝构造函数、拷贝赋值重载。这些涉及对象的创建、销毁和拷贝,是资源管理的核心,需要重点学习。
取地址重载(普通取地址和const取地址)。简单了解即可。
C++11 又增加了2 个默认成员函数:移动构造和移动赋值。
需要掌握的是我们不写时,编译器默认⽣成的函数是什么,是否满⾜我们的需求。
编译器默认⽣成的函数不满⾜我们的需求时,如何⾃⼰实现?
一、构造函数(应写尽写)
构造函数的主要任务不是开空间创建对象(对象空间在栈帧创建时已分配),而是实例化时初始化对象。
其本质是替代 C 语言中手动调用的Init初始化函数,利用“对象实例化时系统自动调用”的特性,让初始化更自动化、更安全。
(一)特点:
函数名与类名相同。
无返回值(不需写
void,这是 C++ 的强制规定)。对象实例化时系统自动调用。
支持函数重载(可提供多种初始化方式)。
什么是“默认构造函数”?不传实参就可以调用的构造函数。包含三类:
我们写的无参构造函数。
我们写的全缺省构造函数(所有参数都有默认值)。
编译器自动生成的默认构造函数。
注意:这三者只能存在一个。因为无参和全缺省虽构成重载,但调用时(如
Date d1;)编译器无法区分,会产生歧义报错。
如果没写任何构造函数,编译器会自动生成一个无参默认构造;如果写了任意构造函数(无论是有参、无参还是缺省),编译器不再生成。
编译器生成的默认构造做了什么?
C++ 区分类型来处理成员变量:
内置类型(
int、char、指针等):不要求初始化,其初始值通常是随机值(取决于编译器)。自定义类型(类/结构体对象):必须调用该成员变量自身的默认构造函数来初始化。
若该自定义类型成员没有默认构造函数(即没有无参构造/全缺省构造,且编译器也未生成),则会编译报错。
解决方案:需使用初始化列表。
(二)代码示例
对象定义陷阱:调用无参构造创建对象时,对象后面不要跟括号(即写
Date d1;)。若写成Date d3();,编译器会将其识别为函数声明,而不是创建对象。自动调用传递:如示例中的
MyQueue类,它包含两个Stack自定义成员。即使MyQueue没写构造函数,编译器生成的默认构造也会自动调用Stack的构造函数,完美完成嵌套对象的初始化。class Date { public: // 1.⽆参构造函数 //Date() //{ // _year = 1; // _month = 1; // _day = 1; //} // 2.带参构造函数 //Date(int year, int month, int day) //{ // _year = year; // _month = month; // _day = day; //} // 3.全缺省构造函数 Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } void Print() { cout << _year << "/" << _month << "/" << _day << endl; } private: int _year; int _month; int _day; }; int main() { // 如果留下三个构造中的第⼆个带参构造,第⼀个和第三个注释掉 // 编译报错:error C2512 : “Date”:没有合适的默认构造函数可⽤ Date d1;// 调⽤默认构造函数 Date d2(2025, 1, 1); // 调⽤带参的构造函数 // 注意:如果通过⽆参构造函数创建对象时,对象后⾯不⽤跟括号,否则编译器⽆法区分这⾥是函数声明还是实例化对象 // warning C4930: “Date d3(void)”: 未调⽤原型函数(是否是有意⽤变量定义的? ) Date d3(); d1.Print(); d2.Print(); return 0; }typedef int STDataType; class Stack { public: Stack(int n = 4) { _a = (STDataType*)malloc(sizeof(STDataType) * n); if (nullptr == _a) { perror("malloc申请空间失败"); return; } _capacity = n; _top = 0; } // ... private: STDataType * _a; size_t _capacity; size_t _top; }; // 两个Stack实现队列 class MyQueue { public: //编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化 private: Stack pushst; Stack popst; }; int main() { MyQueue mq; return 0; }二、析构函数
析构函数不是销毁对象本身(局部对象占用的栈帧随函数结束自动释放,不需我们管)。
在对象销毁时自动调用,用于完成对象中资源的清理释放(如动态内存、文件句柄等)。
等价于 C 语言中 Stack 的
Destroy函数;而像Date类这种没有资源申请的,严格来说不需要析构函数。
(一)特点:
函数名:类名前加
~(如~Stack())。参数与返回值:无参数、无返回值(不需要写
void)。唯一性:一个类只能有一个析构函数(不可重载)。
触发时机:对象生命周期结束时,系统自动调用。
编译器自动生成的析构函数做了什么?内置类型(
int、指针等):不做处理(不释放资源);自定义类型成员(其他类对象):自动调用该成员自身的析构函数。即使你显式写了析构函数,类中的自定义类型成员依然会自动调用其自身的析构函数
何时该写,何时不用写?
不用写(使用编译器默认生成):
类中没有申请资源(如
Date)。默认生成的析构够用,如
MyQueue,它只包含Stack对象,默认析构会自动调用Stack的析构,释放干净。必须显式写:
类中动态申请了资源(如
Stack中用了malloc开空间)。如果不写,默认析构不会释放_a指向的内存,会造成内存泄漏。
8.局部对象的析构顺序:同一个作用域内有多个对象时,遵循后定义、先析构的原则(构造顺序的逆序)。
(二)代码示例:
Stack:动态申请了空间,显式写了析构,内部free(_a),防止泄漏。MyQueue:仅包含两个Stack对象,无需显式写析构,编译器生成的默认析构会自动调用Stack的析构,正确完成嵌套资源的释放。typedef int STDataType; class Stack { public: Stack(int n = 4) { _a = (STDataType*)malloc(sizeof(STDataType) * n); if (nullptr == _a) { perror("malloc申请空间失败"); return; } _capacity = n; _top = 0; } ~Stack() { cout << "~Stack()" << endl; free(_a); _a = nullptr; _top = _capacity = 0; } private: STDataType* _a; size_t _capacity; size_t _top; }; // 两个Stack实现队列 class MyQueue { public: //编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源 // 显⽰写析构,也会⾃动调⽤Stack的析构 /*~MyQueue() {}*/ private: Stack pushst; Stack popst; }; int main() { Stack st; MyQueue mq; return 0; }三、拷贝构造函数
定义:第一个参数是自身类类型的引用(通常为
const修饰),且其余参数均有默认值的构造函数。本质:构造函数的一种重载形式,用于用已存在的对象初始化同类型的新对象。
(一)特点
1.参数必须为引用:若使用传值方式(如Date(Date d)),编译器会报错,(C++规定传值传参会调用拷贝构造,导致无限递归。)(传值调用拷贝构造,调用拷贝构造需要传值,传值又要调用拷贝构造)
为什么会发生无限递归?
假设我们写了错误的拷贝构造:Date(Date d),并执行代码 Date d2(d1);(用 d1 拷贝构造 d2)。编译器和运行时的逻辑推演是这样的:
调用起点:我们要用 d1 拷贝构造 d2,所以需要调用拷贝构造 Date(Date d)。
传值困境(第一次递归):这个函数的参数是 Date d,它是一个值传递的参数。C++ 规定,值传递自定义类型时,形参 d 需要用实参 d1 来拷贝初始化。
触发新拷贝:为了完成“把 d1 拷贝给形参 d”这个动作,编译器必须再次调用拷贝构造函数。
回到起点:再次调用的拷贝构造,依然是 Date(Date d),它的参数依然是值传递。
死循环:为了进入第二次调用,又得完成它的传值传参,于是又触发第三次调用……如此层层嵌套,永远不会到达函数体内部,直到编译器栈溢出或直接报错(C++标准规定编译器必须拒绝这种代码)。
为什么改成引用 (const Date& d) 就解决了?
因为引用是别名,不是值。当你写 Date(const Date& d) 时,形参 d 只是实参 d1 的一个别名(类似于给 d1 起了个小名)。传递引用不需要拷贝对象,它只是传递了一个地址。因此,编译器不需要调用任何拷贝构造就能完成传参,直接进入了函数体内部,递归链条就此打断。
可以有额外参数,但必须提供默认值(如Date(const Date& d, int n = 0))。
调用时机:自定义类型对象的拷贝行为(传值传参、传值返回、显式拷贝初始化)都会触发拷贝构造。
2.编译器自动生成的拷贝构造
若用户未显式定义,编译器会自动生成一个默认拷贝构造。
默认行为:
对内置类型成员:执行浅拷贝(逐字节复制)。(比如日期类就不需要我们显式的写拷贝构造函数)
对自定义类型成员:调用该成员对应的拷贝构造。
何时需要显式定义:
类成员指向动态分配的资源(如 Stack 中的 _a),浅拷贝会导致多个对象共享同一资源,比如析构时重复释放同一块空间 → 程序崩溃,入栈时互相覆盖对方资源。此时必须显式实现深拷贝(为指针成员重新分配空间并复制内容)。
经验技巧:若类显式定义了析构函数来释放资源,则通常也需要显式定义拷贝构造(以及拷贝赋值)。
3. 深拷贝 vs 浅拷贝
浅拷贝:仅复制指针值,不复制指向的资源。
深拷贝:复制指针所指向的资源内容,确保每个对象拥有独立的资源副本。
典型场景:
Date(成员均为内置类型,无资源)→ 无需显式拷贝构造,默认即可。
Stack(含动态数组)→ 必须显式深拷贝。
MyQueue(成员为 Stack 对象)→ 编译器自动生成拷贝构造时会调用 Stack 的深拷贝,因此无需再显式实现。
4. 传值返回与引用返回
传值返回:会产生一个临时对象,调用拷贝构造。性能开销较大。
传引用返回:返回对象的别名(引用),避免拷贝,效率高。
⚠️ 关键风险:绝不能返回局部对象的引用(函数结束局部对象销毁,引用成为“野引用”)。只有确保返回对象在函数结束后仍存在(如静态、全局、或通过参数传入的引用)时,才可使用引用返回。
(二)代码示例
// 正确的拷贝构造(const 引用) Date(const Date& d) { _year = d._year; _month = d._month; _day = d._day; } // 错误的写法(值传递,报错) // Date(Date d) { ... } // 深拷贝示例(Stack) Stack(const Stack& st) { _a = (STDataType*)malloc(sizeof(STDataType) * st._capacity); memcpy(_a, st._a, sizeof(STDataType) * st._top); _top = st._top; _capacity = st._capacity; }class Date { public: Date(int year = 1, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } // 编译报错:error C2652 : “Date”:⾮法的复制构造函数:第⼀个参数不应是“Date” //Date(Date d) 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; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; void Func1(Date d) { cout << &d << endl; d.Print(); } // Date Func2() Date& Func2() { Date tmp(2024, 7, 5); tmp.Print(); return tmp; } int main() { Date d1(2024, 7, 5); // C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥传值传参要调⽤拷⻉构造 // 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉,传引⽤传参可以较少这⾥的拷⻉ Func1(d1); cout << &d1 << endl; // 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造 Date d2(&d1); d1.Print(); d2.Print(); //这样写才是拷⻉构造,通过同类型的对象初始化构造,⽽不是指针 Date d3(d1); d2.Print(); // 也可以这样写,这⾥也是拷⻉构造 Date d4 = d1; d2.Print(); // Func2返回了⼀个局部对象tmp的引⽤作为返回值 // Func2函数结束,tmp对象就销毁了,相当于了⼀个野引⽤ Date ret = Func2(); ret.Print(); return 0; }四、运算符重载
1. 什么是运算符重载?
给类类型的对象使用运算符时(如d1 == d2),编译器会将其转换为调用对应的operator函数。如果没有重载,编译会报错。
2. 重载的语法规则
函数名:
operator后加上运算符类似于函数名,例如operator==。参数个数:与运算符操作数数量一致。
一元运算符(如
++):1个参数。二元运算符(如
+、==):2个参数,左侧操作数传给第1个参数,右侧传给第2个。
成员函数特例:如果重载为类的成员函数,第一个操作数会默认传给隐藏的
this指针,所以参数列表少一个。
3. 重要限制(高频考点)
不能创建新运算符:如
operator@是不允许的。以下5个运算符不能重载(死记硬背):
.*::sizeof?: .
必须有一个类类型参数:不能改变内置类型的运算含义(比如你不能重载
int + int)。优先级和结合性不变:重载后,运算符的优先级依然和内置类型一样。
6. 访问私有成员的问题
如果重载为全局函数,直接访问对象的私有成员会报错。解决办法:
把成员变量设为公有(不推荐)。
提供公有的
GetXXX接口。使用友元函数(常用)。
直接重载为成员函数(省去友元,但要注意
<<不能用此法)。
7、赋值运算符重载(核心重点)
赋值运算符重载是一个特殊的默认成员函数,用于两个已经存在的对象之间的拷贝赋值。
7.1 必须遵守的三大特点
7.必须重载为成员函数。
参数建议:写成
const 类名&(传引用避免拷贝,加 const 防止修改右侧对象)。void operator=(Date d)//这样也可以,不过传值传参会有拷贝,会出现额外开销,但不会出现嵌套调用拷贝构造 {}返回值建议:写成
类名&(返回当前对象的引用),目的是支持连续赋值(如d1 = d2 = d3)。传值返回会创建临时对象,调用拷贝构造,产生拷贝开销。
传引用返回不产生拷贝,返回的是对象的别名,但必须确保返回的对象在函数结束后依然有效(如全局变量、静态变量、成员变量或调用方传入的引用);若返回的是函数内的局部对象,则会导致野引用,危险。简言之:引用返回高效但需保证对象生命周期,局部对象不可用引用返回。
7.2 编译器自动生成的默认行为
如果没写,编译器会自动生成一个。
默认行为:(与拷贝构造类似)
对内置类型成员:完成浅拷贝(逐字节复制)。
对自定义类型成员:调用该成员的赋值运算符重载。
7.3 什么时候需要自己实现?(关键判断技巧)
像
Date类(只有内置类型,不指向资源):默认生成的完全够用,不需要自己写。像
Stack类(有动态申请的_a指针):默认浅拷贝会导致两个对象指向同一块内存,析构时崩溃。必须自己实现深拷贝。像
MyQueue类(成员全是Stack类型):因为编译器会调用Stack的赋值重载,所以MyQueue不需要自己写。技巧:如果一个类显式写了析构函数来释放资源,那么这个类大概率也需要显式写赋值运算符重载(和拷贝构造)。
7.4"赋值" vs "拷贝构造"
| 比较维度 | 拷贝构造 | 赋值重载 |
|---|---|---|
| 发生时机 | 对象还未创建,用已有对象初始化新对象。 | 对象已经存在,用另一个对象覆盖它。 |
| 代码示例 | Date d2 = d1;(d2 还没创建) | d1 = d3;(d1 已经创建好了) |
| 是否生成新对象 | 是(创建新对象) | 否(修改已有对象) |
Date d1(2026, 1, 1); Date d2(2026, 1, 2); //赋值运算符重载 d2 = d1; //拷贝构造 Date d3(d2); Date d4 = d2;代码示例:日期类
Date.h
#pragma once #include<iostream> using namespace std; class Date { public: //友元函数声明,加在public,或者public,private之外的位置(即private)也可以,加在哪儿都可以 friend ostream& operator<<(ostream& out, const Date& d);//支持连续的输出需要加引用返回ostream& friend istream& operator>>(istream& in, Date& d); int getMonthDay(int year, int month)//获取当前月份天数 { static int MonthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0))//闰年的2月有29天,其他年份2月为28天 //后两个条件是一个整体,必须加括号,(4年一润百年不润 或 四百年一润) { return 29; } return MonthDayArray[month]; } Date(int year = 1900, int month = 1, int day = 1); void Print() const; //检查日期是否合法 bool CheckDate(); // d1 + 天数 Date operator+(int day) const; Date& operator+=(int day); //Date operator-(int day); //d1-天数 Date& operator-=(int day); Date operator-(int day) const; //++ Date& operator++();//++d1 Date operator++(int);//d1++,加一个参数区分前置还是后置加加 //-- Date& operator--();//--d1 Date operator--(int);//d1-- //比较大小 bool operator==(const Date& d) const; bool operator<(const Date& d) const; bool operator<=(const Date& d) const; bool operator>(const Date& d) const; bool operator>=(const Date& d) const; bool operator!=(const Date& d) const; int operator-(const Date& d) const; //ostream& operator<<(ostream& out)//支持连续的输出需要加引用返回ostream& //{ // out << _year << "年" << _month << "月" << _day << "日" << endl; // //二元运算符,左操作数传递给第一个对象,右操作数传递给第二个对象 // //d1<<cout这样才对,d1传给this指针,cout传给out // //但这样可读性比较差,只能被迫重载为全局函数 // return out; //} ////属于默认成员函数,不显示式写,会默认生成(库里面有,可以不写) //Date* operator&()//与底下函数构成重载,返回值与参数(隐含的this指针)都不同,一个是普通对象Date*,一个是const对象const Date* //{ // return this; // // return nullptr; //} //const Date* operator&()const //{ // return this; // // return nullptr; //} private: int _year; int _month; int _day; }; //重复声明没事 ostream& operator<<(ostream& out, const Date& d);//支持连续的输出需要加引用返回ostream& istream& operator>>(istream& in, Date& d);//d是形参,Date& d加引用形参d改变,外面也改变;提取的日期类值要放在对象d中,d得可写,不能加const,Date.cpp
#include"Date.h" bool Date::CheckDate() { if(_month < 1 || _month > 12 || _day <1 || _day > getMonthDay(_year, _month)) { return false; } else { return true; } } Date::Date(int year, int month, int day) { _year = year; _month = month; _day = day; if (!CheckDate())//if(!(this->CheckDate())) { cout << "非法日期" << *this <<endl; } } void Date::Print() const { cout << _year << "-" << _month << "-" << _day << endl; } Date Date::operator+(int day) const { Date tmp = *this; tmp += day;//直接用写好的+=重载 //Date tmp = *this; //tmp._day += day; //while (tmp._day > getMonthDay(tmp._year, tmp._month)) //{ // tmp._day -= getMonthDay(tmp._year, tmp._month); // tmp._month++; // if (tmp._month == 13) // { // tmp._month = 1; // tmp._year++; // } //} return tmp; } Date& Date::operator+=(int day) { if (day < 0)//d1 += -100(处理这种情况) { return *this -= -day; } _day += day; while (_day > getMonthDay(_year, _month)) { _day -= getMonthDay(_year, _month); _month++; if (_month == 13) { _month = 1; _year++; } } return *this; } //日期-天数 //2025.4.26 26-100=-74 //2025.4.-74 -74+31=-43 //2025.3.-43 -43+28=-15 //20252.-15 -15+31=16 //2025.1.16 Date& Date::operator-=(int day) { if (day < 0) { return *this += -day; } _day -= day;//26-100=-74 while (_day <= 0)//4.0那就不对了 { --_month; if (_month == 0) { _month = 12; --_year; } _day += getMonthDay(_year, _month);//加上减去的那个月天数 } return *this; } Date Date::operator-(int day) const { Date tmp(*this); tmp -= day; return tmp; } Date& Date::operator++()//++d1,出了作用域还在,可以用引用返回 { *this += 1; return *this; } Date Date::operator++(int)//d1++,出了作用域tmp不在,不能用引用返回 { Date tmp(*this); *this += 1; return tmp; } Date& Date::operator--()//--d1 { *this -= 1; return *this; } Date Date::operator--(int)//d1-- { Date tmp(*this); *this -= 1; return tmp; } bool Date::operator==(const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; } bool Date::operator<(const Date& d) const { 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 Date::operator<=(const Date& d) const { return *this < d || *this == d; } bool Date::operator>(const Date& d) const { return !(*this <= d);//<=取反 } bool Date::operator>=(const Date& d) const { return !(*this < d); } bool Date::operator!=(const Date& d) const { return !(*this == d); } int Date::operator-(const Date& d) const//把小的那个日期一天一天往大的加,加了多少次就差多少天 { Date max = *this; Date min = d; int flag = 1; if (*this < d) { max = d; min = *this; flag = -1; } int n = 0; while (min != max) { ++min; ++n; } return n*flag;//d1<d2,flag=1,天数为正;d1>d2,flag-1,天数为负 } //类外想访问类内私有成员变量,用友元函数,加在哪里都可以,这里放在声明位置 ostream& operator<<(ostream& out, const Date& d)//支持连续的输出需要加引用返回ostream& { out << d._year << "年" << d._month << "月" << d._day << "日" << endl; return out; } istream& operator>>(istream& in, Date& d) { while(1) { cout << "请依次输入年月日"; in >> d._year >> d._month >> d._day; if (d.CheckDate()) { break; } else { cout << "非法日期,请重新输入" << endl; } } return in; }Test.cpp
#include"Date.h" void TestDate1()//测试+=,+ { Date d1(2026, 8, 1); Date d2(2026, 8, 2); d1.Print(); Date d3(d1 + 10000); d1 += 10000; d3.Print(); d1.Print(); d1 += -100; d1.Print(); } void TestDate2()//测试-=,- { Date d1(2026, 10, 22); Date d2 = d1 -= 10000; d1.Print(); d2.Print(); Date d3(2026, 10, 22); Date d4 = d3 - 10000; d4.Print(); d1 -= -100; d1.Print(); } void TestDate3()//测试++,-- { Date d1(2026, 10, 22); Date d2 = d1++; //Date d2 = d1.operator++(1);//显式调用,一般不这样用 d1.Print(); d2.Print(); Date d3(2026, 10, 22); Date d4 = ++d2; d3.Print(); d4.Print(); } void TestDate4() { Date d1(2026, 10, 22); Date d2(2026, 11, 22); cout << (d1 < d2) << endl;//必须要加括号 cout << (d1 > d2) << endl; cout << (d1 == d2) << endl; } void TestDate5() { Date d1(2025, 4, 26); Date d2(2025, 7, 1); cout << d1 - d2 << endl;//d1 - d2不需要加括号,-的优先级比较高 } void TestDate6() { Date d1(2025, 4, 26); Date d2(2025, 7, 1); //int i = 1; //double d = 1.1; //cout << i << " " << d << endl; //之所以可以自动识别输出变量类型,是因为iostream库中把<<重载好了 //cout.operator<<(i);//等价于调用这个函数 //流插入运算符 cout << d1;//operator<< operator <<(cout, d1); cin >> d1 >> d2; cout << d1 << d2; cout << d1 - d2 << endl; } void TestDate7() { Date d1(2025, 4, 26); d1.Print(); const Date d2(2025, 7, 1); //d2.Print();错误 } void TestDate8() { Date d1(2025, 4, 26); Date d2(2025, 7, 1); cout << &d1 << endl; cout << &d2 << endl; } int main() { //TestDate1(); //TestDate2(); //TestDate3(); //TestDate4(); //TestDate5(); //TestDate7(); TestDate8(); return 0; }8.流插入运算符
int i = 1; double d = 1.1; cout << i << " " << d << endl; //之所以可以自动识别输出变量类型,是因为iostream库中把<<重载好了 //cout.operator<<(i);//等价于调用这个函数9.const成员函数
将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后 ⾯。
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。 const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this
const Date* const this 那么这两个this有什么区别呢?
| 写法 | 名字 | 能不能改变指针指向? | 能不能改变指向的内容? | 比喻 |
|---|---|---|---|---|
Date* const this | 常量指针 | 不能 | 能 | 默认this指针都是这样,因为如果this的指向可以更改 this = nullptr; // 灾难! this->_year = 2026; // 程序直接崩溃 (空指针解引用) |
const Date* p | 指针常量 (指向常量的指针) | 能 | 不能 | p指向的内容只能读,不能写 |
| 两者皆是 | 不能 | 不能 |
10.取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动 ⽣成的就可以够我们⽤了,不需要去显⽰实现。除⾮⼀些很特殊的场景。
//属于默认成员函数,不显示式写,会默认生成(库里面有,可以不写) Date* operator&()//与底下函数构成重载,返回值与参数(隐含的this指针)都不同,一个是普通对象Date*,一个是const对象const Date* { return this; // return nullptr; } const Date* operator&()const { return this; // return nullptr; }void TestDate8() { Date d1(2025, 4, 26); Date d2(2025, 7, 1); cout << &d1 << endl; cout << &d2 << endl; }