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

posted @ 2020-06-19 14:07  zJanly  阅读(348)  评论(0编辑  收藏  举报