从零开始搞定C++类和对象:取地址运算符重载
const成员函数
我们还是用我们之前常用的日期类来举例子,我们来看下面一个代码:
#include<iostream>
using namespace std;//创建日期类
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}Date(int year=1,int month=2,int day=3){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2025,9,14);d1.Print();return 0;
}
看一下运行结果:
可以看到没有问题,我们再来看一下下面这一个代码:
可以看到下面这一个代码发生了报错,我们现在来分析一下报错的原因:
我们知道,当我们调用类里面的成员函数时,成员函数的第一个形参是this指针,实参就是调用函数的对象的地址,this指针的类型是Date *const this,this被const修饰,表示不能改变this指针的指向,但是还是可以通过this指针改变它所指向的对象中的成员的内容,比如我们可以通过this指针改变对象d1中的成员变量。
我们再来看到d2这个对象,他被const修饰,&d2是什么类型呢?类型是const Date*,&d2的类型决定了不能通过指针改变d2中的内容,但是当我们将&d2作为实参传递给this指针的时候,就发生了权限的放大这一问题。
既然发现了问题,那就要解决问题,我们直接将this指针的类型改成const Date*const this不就行了吗?
但是this指针是隐含的呀,我们并不能在参数列表中直接修改它的类型。
这就引入了我们这一节要讲的知识点——const成员函数。
- 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
- const实际修饰的是const成员函数中的this指针,表明在该成员函数中不能对类的任何成员进行修改。
上述例子中,const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this。
所以我们可以将代码进行如下修改,使得Print变为const成员函数。
#include<iostream>
using namespace std;//创建日期类
class Date
{
public:void Print()const{//在const成员函数中,不能修改成员变量//比如://_year++;cout << _year << "-" << _month << "-" << _day << endl;}Date(int year = 1, int month = 2, int day = 3){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2025, 9, 14);d1.Print();const Date d2(2025, 9, 14);d2.Print();return 0;
}
将成员函数进行const修饰以后,就变得比较方便了:不管是被const修饰的对象调用const成员函数还是普通变量调用这个函数都是可以的,既然const如此方便,我们是不是就可以对所有的成员函数加上const修饰呢?
大家万万不能这么想,因为const成员函数不能改变成员变量,这就十分的不方便。就像构造函数,我们在定义全缺省构造函数的时候就会在函数体内改变对象中的成员变量。说到这里,不知道大家发现一个问题没有,在讲const成员函数之前,我们调用构造函数对d2对象进行初始化的时候,竟然不会报错,而构造函数并没有被const修饰,里面的this指针的类型还是Date* const this,这是咋回事呢?
这里就需要特殊说明一下了,我们可以认为const对象在初始化之前是没有const属性的,只有在初始化完成以后才具有const属性,而我们的构造函数就是为了完成类的对象的初始化,所以构造函数不需要成为const成员函数。
总结一句话就是如果在成员函数内部我们不需要修改对象的成员变量,就可以加上const进行修饰。
取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显式实现。
但我们还是来演示一下如何自己写一个取地址运算符重载函数,我们还是用日期类来打比方:
#include<iostream>
using namespace std;//创建日期类
class Date
{
public:void Print()const{//在const成员函数中,不能修改成员变量//比如://_year++;cout << _year << "-" << _month << "-" << _day << endl;}Date(int year = 1, int month = 2, int day = 3){_year = year;_month = month;_day = day;}Date(Date& d){_year = d._year;_day = d._day;_month = d._month;}//取地址运算符重载函数Date* operator&(){return this;}const Date* operator&()const{return this;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2025, 9, 14);d1.Print();const Date d2 = d1;//d2 = d1;d2.Print();Date* p1 = &d1;const Date* p2 = &d2;cout << p1 << endl;cout << p2 << endl;return 0;
}
可以看到,取地址运算符重载函数是十分简单的,一般我们也不需要自己去实现,除非有一些特殊情况,比如要求实现一个类,我们无法得到这个类中对象的真实地址,此时我们就可以自己去写一个取地址运算符重载:
#include<iostream>
using namespace std;//创建日期类
class Date
{
public:void Print()const{//在const成员函数中,不能修改成员变量//比如://_year++;cout << _year << "-" << _month << "-" << _day << endl;}Date(int year = 1, int month = 2, int day = 3){_year = year;_month = month;_day = day;}Date(Date& d){_year = d._year;_day = d._day;_month = d._month;}//取地址运算符重载函数Date* operator&(){return nullptr;}const Date* operator&()const{return nullptr;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2025, 9, 14);d1.Print();const Date d2 = d1;//d2 = d1;d2.Print();Date* p1 = &d1;const Date* p2 = &d2;cout << p1 << endl;cout << p2 << endl;return 0;
}
小结:这一节的内容还是相当简单的,学到这,我们终于讲完了类与对象(中)的全部内容,小伙伴们还是要多敲代码勤加练习哦。