const_cast
避免未定义行为(使用引用时的未定义行为)
一个变量在使用const_cast去掉指针或者引用的const限定符后,“如果常量本身不是常量,获得的权限是合法的, 如果本身是常量,使用const_cast再写的后果是未定义的。”
int main(){
const int a = 1;
int & b = const_cast<int&>(a);
b = 2;
cout << a ;
cout << b;
}
1
2
3
4
5
6
7
虽然上面程序运行没有问题,但定义const常量的初衷肯定就是为了不修改,所以出现了上述情况肯定是程序设计有问题。
在这里插入图片描述
而且你会发现调试窗口中,两个变量的值按照我们所想,但打印时,却是先打印1,后打印2。这是因为常量折叠。
常量折叠
将上面代码进行反汇编。
const int a = 1;
00971F72 C7 45 F4 01 00 00 00 mov dword ptr [a],1
int & b = const_cast<int&>(a);
00971F79 8D 45 F4 lea eax,[a] //可以发现,引用本质是指针,这里把a的地址先存到eax
00971F7C 89 45 E8 mov dword ptr [b],eax //再把地址赋值给b
b = 2;
00971F7F 8B 45 E8 mov eax,dword ptr [b] //先把地址给eax
00971F82 C7 00 02 00 00 00 mov dword ptr [eax],2 //中括号里面放的是地址
cout << a ;
00971F88 8B F4 mov esi,esp
00971F8A 6A 01 push 1 //打印的时候,直接入栈的是1,这里发生了常量折叠
00971F8C 8B 0D D4 D0 97 00 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (097D0D4h)]
00971F92 FF 15 E4 D0 97 00 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (097D0E4h)]
00971F98 3B F4 cmp esi,esp
00971F9A E8 0E F3 FF FF call __RTC_CheckEsp (09712ADh)
cout << b;
00971F9F 8B F4 mov esi,esp
00971FA1 8B 45 E8 mov eax,dword ptr [b]
00971FA4 8B 08 mov ecx,dword ptr [eax]
00971FA6 51 push ecx
00971FA7 8B 0D D4 D0 97 00 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (097D0D4h)]
00971FAD FF 15 E4 D0 97 00 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (097D0E4h)]
00971FB3 3B F4 cmp esi,esp
00971FB5 E8 F3 F2 FF FF call __RTC_CheckEsp (09712ADh)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
常量折叠就是,在编译阶段,对该变量进行值替换。类似宏定义。
使用指针时的未定义行为
int main(){
const int a = 1;
int * b = const_cast<int*>(&a);
*b = 2;
cout << &a <<endl;
cout << b << endl;
cout << a << endl;
cout << *b << endl;
}
1
2
3
4
5
6
7
8
9
在这里插入图片描述
使用const_cast去掉const限定符
只有当对象原本就是非常量时,才是正确的行为。
void func(const int& a)//形参为,引用指向const int
{
int& b = const_cast<int&>(a);//去掉const限定,因为原本为非常量
b++;
return;
}
int main()
{
int a = 100;
func(a);
cout << a << endl; // 打印101
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
此函数的形参本来设置为int& a就好,但这里只是为了体现用法。
使用const_cast添加const限定符
const string& shorter(const string& s1, const string& s2) {
return s1.size() <= s2.size() ? s1 : s2;
}
string& shorter(string& s1, string& s2) {
//重载调用到上一个函数,它已经写好了比较的逻辑
auto &r = shorter(const_cast<const string&>(s1), const_cast<const string&>(s2));
//auto等号右边为引用,类型会忽略掉引用
return const_cast<string&>(r);
}
1
2
3
4
5
6
7
8
9
10
const string&版本函数为比较字符串长度的正确实现,因为比较长度时不会改变字符串,所以参数和返回值都为const。
当实参为const时,由于函数重载,会调用到const string&版本函数,这也是期望的结果。
但当实参为非const时,我们希望还是继续调用已经写好比较逻辑的const string&版本函数,但返回值希望返回string &,所以这里再封装一层函数。(这里一个知识点,函数重载是忽略返回值,且忽略形参的顶层const,可以这么理解,有顶层const的概念,说明参数是引用或指针,但我们主要关心的是指向的数据,所以要忽略顶层const)(因为参数忽略顶层const,所以重载时只关心底层const是否一样)
————————————————
版权声明:本文为CSDN博主「anlian523」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/anlian523/java/article/details/95751762