目录
- const成员函数
- const对象不可以调用非const成员函数
- 非const对象可以调用const成员函数
- const成员函数内不可以调用其它的非const成员函数
- 非const成员函数内可以调用其它的const成员函
- 取地址及const取地址操作符重载
- const补充
- 场景1
- 场景2
- 场景3
- 场景4
const成员函数
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
const对象不可以调用非const成员函数
这是Print函数,当我们用const修饰的对象去调用他时
void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
const修饰对象报错常见的就是权限的放大问题
d1被const修饰后是不可以修改,而Print函数传参时是隐藏了this指针的,也就是说print函数传的参数是d1的地址,将d1的地址穿进去后就有可能会修改d1,所以这是权限放大的原因
要解决问题就需要用const去修饰函数
void Print()const
{cout << _year << "/" << _month << "/" << _day << endl;
}
这里的const是修饰this指针指向的内容,注意const修饰的方式是void Print()const,而不是const void Print()或void const Print()
如果对于全局函数就不能像上面修饰的一样了
void operator <<(ostream& out) 不能修饰成 operator <<(ostream& out)const
void operator >>(ostream& in,Date &d) 不能修饰成 void operator >>(ostream& in,Date &d)const
要搞清楚const是想要修饰什么,void Print()const修饰的是隐藏的this指针
operator <<(ostream& out)和 void operator >>(ostream& in,Date &d)根本就没有隐藏的this指针,所以不需要在后面加const
即使想要加const也要这样加 void operator >>(ostream& in,const Date &d)(但是这样是错误的,因为流提前要改变对象,所以不应该加const,这里只是演示该加在哪)
非const对象可以调用const成员函数
非const对象可以调用const成员函数是因为d1没有被const修饰,所以他是可读可写的,而Print函数被const修饰后只要求可以读取数据内容,并不要求修改数据,所以当然可以,可以把这里理解成权限的缩小
要注意对于只要求读取数据的函数可以加上const修饰,但不用让所以函数都被const修饰,因为如果有的函数要求修改数据,加上const后就会出现权限放大问题
总结
成员函数中如果是一个对成员变量只进行读访问的函数建议加上const修饰,这样被const修饰的对象和没变const修饰的对象都可以使用
如果是对成员变量进行读写访问的函数,不能加上const修饰,否则会出现权限放大问题
const成员函数内不可以调用其它的非const成员函数
这里还是权限放大问题,const修饰的成员函数表示这个函数内部都是不可以修改Date的成员变量的,而如果里面出现了非const的成员函数,就表示里面可以修改成员变量,这显然不行
非const成员函数内可以调用其它的const成员函
这是权限缩小,所以可以
总之const修饰后的对象注意一下权限是否变大就行了
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容
class Date
{
private:int _year;
};
class A
{
public:A* operator&(){return this;}const A* operator&()const{return this;}
};
int main()
{A aa1;const A aa2;cout << &aa1 << endl;cout << &aa2 << endl;return 0;
}
A* operator&()和const A* operator&()const是想返回this指针,而返回的this指针有差别
A* operator&()对传入的this指针没有进行const修饰,并且返回的this指针也没有进行const修饰
而const A* operator&()const对传入的this指针进行了修饰,并且返回的this指针也进行了const修饰
通过调用这两个函数得到的this指针可以直接打印出this指针的值
当我们屏蔽掉上面的代码后,也可以正常运行,因为这是默认成员函数,我们不写编译器会生成,而这两个默认成员函数并不像我们之前写的拷贝函数和析构函数等等默认成员函数那样复杂,这两个默认成员函数就是返回一个this指针,所以日常都不需要我们写,编译器默认生成的函数就够用了
那什么时候是需要我们写的呢?
比如我们只想让被const修饰后的成员函数拿到地址,这样做的目的就是不想有人拿到地址后乱搞
class Date
{
private:int _year;
};
class A
{
public:A* operator&(){return nullptr;}const A* operator&()const{return this;}
};
int main()
{A aa1;const A aa2;cout << &aa1 << endl;cout << &aa2 << endl;return 0;
}
甚至我们还可以返回一个假地址
const补充
场景1
int main()
{const int i = 0;int j = i; cout << j << endl;return 0;
}
这里j=i没有报错是因为j是i的拷贝,将i的值拷贝给了j
场景2
int main()
{const int i = 0;int& r = i; cout << r << endl;return 0;
}
这里的r是i的别名,r被修改会导致i也会被修改,所以报错
场景3
int main()
{int i = 0;const int* p1 = &i;int* p2 = p1;return 0;
}
const修饰p1表示不可以通过p1去修改p1指向的值i,换句话来说就是不可以修改p1
但是可以修改p1指向的地址,也就是p1可以被修改
上面的代码中 int p2 = p1是想将p1指向的地址传给p2,而p1指向的地址是&i
根据前面的结论,const修饰p1表示p1不可以被修改,而p1将i的地址给了p2,p2就可以通过p1给的地址去修改i,也就间接的使*p1修改了,所以才会报错
场景4
int main()
{int i = 0;int* const p1 = &i;int* p2 = p1;return 0;
}
const修饰p1表示p1不可以被修改,因为p1是一个指针,而p1保存的是i的地址,p1不能被修改意思就是i的地址不可以修改
将p1的值给p2,这里并不会报错,我猜测可能p2也是拷贝了p1的地址,所以p2的改变不会影响p1
如果想让上面的代码报错,我们可以修改p1,让p1保存另一个变量j的地址,因为const修饰的是p1,p1是不能变的,所以就会报错