Ⅰ. const成员
两种const
我们知道,用const修饰 能起到保护,使之不被修改的作用。
修饰指针的const有两种位置:
我们学过的this指针,就被后者所修饰,因此无法被修改。
const成员函数
➡️为了保护函数里的成员,使不被修改,C++引入了const成员函数。
“const成员函数” ,就是被const修饰的成员函数。
const的存在,使我们不能对任何成员进行修改。
➡️它长这样:
void Date::Print() const
{……
}
这个函数相当于:
void Date::Print(const Date* const this)
{……
}
❗注意:前后两个const的意义是不一样的。
前者修饰*this,保护 this指向的空间的内容 不被修改。
后者修饰this,保护this指针不被修改。
❓🤔这个const的位置好怪啊,为啥要放在最末尾?而不是开头?
const:爷就长得怪,咋地!
不能放开头啊,那样const就成修饰返回值的了。
➡️对比this指针的类型:
普通函数:类型* const
const成员函数:const 类型* const
➡️权限问题:
就像公司上下级关系,权限大的可以调用权限小的;
而权限小的不可以调用权限大的。
解释这张图:
被const修饰的Print() / d2,因为被限制了内容不能修改,所以权限相对更低。
未被const修饰的Print() / d1,可被修改,自由度更高,权限相对更高。
彼此的关系就用上下级、平级的关系来理解。
所以,const成员函数内部 不可调用 其他非const成员函数;
而非const……可调用……const……。
➡️什么时候加const?
如果一个函数不用修改,性质是只读的,那就加上const来保护。
如果其性质是可读可写,那就不能加。
Ⅱ. 取地址运算符重载
先亮身份:类的六大默认成员函数之一。
函数名:operator&
作用:自定义类型的取地址
引入
来看这个例子。我们知道,自定义类型的运算 需要我们自己去实现 运算符重载,运算符&同样如此。
而这里,我们没写operator&,那究竟能否取地址成功呢?
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(2000, 1, 1);cout << &d1 << endl; //前面没写operator&//能否取地址成功呢?return 0;
}
结果:
取地址成功!
❓But why?
💡这是因为,C++很贴心地将 取地址运算符重载 纳入默认成员函数。
也就是说,operator& 和前几位默认成员函数的福利一样:
如果我们写了,编译器会自动调用;
如果没写,编译器会自动生成,并自动调用。
❓取地址运算符重载长啥样?
Here comes the model:
Date* operator&()
{return this;
}
取地址运算符重载分两种:
1.& 普通对象
2.& const修饰的对象
普通对象取地址
很easy,不详讲了。我们来手动实现一下:
class Date {
public:Date(int year = 1900, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}
Date* operator&() {return this; //this就是它的地址}
private:int _year;int _month;int _day;
};
int main() {Date d1(2000, 1, 1);cout << &d1 << endl;
return 0;
}
const对象取地址
当对象被const修饰时,会特殊一些。
来看d1和d2的对比:
class Date {
public:Date(int year = 1900, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}void Print(){printf("%d-%d-%d\n", this->_year, this->_month, this->_day);}
private:int _year;int _month;int _day;
};
int main() {Date d1(2000, 1, 1); //普通对象d1d1.Print();
const Date d2(2077,1,1); //被const修饰的对象d2d2.Print();
return 0;
}
然而,压根编译不过去:
普通对象d1没问题,而const修饰的d2却亮红牌了,为啥?
其实,const成员报错的原因往往是🤔……权限的放大!
来分析下:
Print函数中,this指针的类型是:Date* const
而d2的类型是:const Date* const
所以说,d2去调用Print(),是一次权限的放大!是以下犯上,大逆不道!
💡对此,解决方案是:
const修饰的对象,应调用const取地址运算符重载
即,d2应调用:
void Print() const{printf("%d-%d-%d\n", this->_year, this->_month, this->_day);
}
什么时候要手动实现operator& ?
我们尽可放宽心,即使是const对象,编译器仍会自动生成 相应的const operator&。
所以无论是 普通对象 还是 const对象 取地址,我们都无需手动实现operator&。
大部分情况下,取地址运算符重载都不用自己写,编译器默认生成的就够用了。
只有特殊情况才需要重载,
如:你不希望别人取到你的地址。那这时就:
Date* operator&(){return nullptr;
}