zoukankan      html  css  js  c++  java
  • 深入C++引用及其注意事项、对引用取地址时的内存模型、const数组等

    const int f[10] = { 1,2,3,4,5,6,7,8,9,10 };
    
    int main()
    {
        // test1
        const int i = 3;
        int& j = const_cast<int&>(i); // 使用const_cast关键字进行强制类型转换
    
        j = 5;
        // 这俩值   会一样吗 ?
        cout << "i = " << i << endl;
        cout << "j = " << j << endl;
    
        // 这俩地址 会一样吗 ?
        cout << "&i = " << &i << endl;
        cout << "&j = " << &j << endl;
        // 实验小结:打印的i和j的地址一样,但是i和j的值却不一样,说明什么?
        //  一个内存单元上不可能有两个不同的值!这里肯定涉及到两个内存单元,而不是一个!即编译器内部做了手脚。
        //  结论:这个最复杂,直接看最后的结论。
    
    
        // test2  探究 --- 对引用取地址 
        int y = 89;
        int& x = y;
        // 这俩地址  会一样吗 ? 
        cout << "&x = " << &x << endl;
        cout << "&y = " << &y << endl;
        // 答: 当然一样 。 因为引用就是别名而已,对同一个内存空间的别名。 
    
        // test3
        //test3 - PART1   本步骤证明了z是只读变量,因为可以通过其地址修改其值
        const int& z = 99;
        int* p = const_cast<int*> (&z);
        *p = 88;
        cout << "z = " << z << endl;
    
        // test3 - PART2  本步骤证明了上面定义的i不是只读变量,而是常量。
        int* p1 = const_cast<int*> (&i);
        *p1 = 88;
        cout << "i = "<< i << endl;
    
    /*
    个人小结:
    1:对常量使用extern关键字或者是&操作符时,内存中会新生成一份该常量对应的变量。
    2:引用,是对某个内存空间冠以别名,对应的就是变量,变量才占有内存空间。而常量存储在符号表内,是没有地址的。  
    3:当对引用取地址,取到的地址是某个内存空间的地址。 
    4:引用的本质是别名,如果此时已经存在了内存空间,那么之后的所有引用都只是该内存空间的别名,并不会涉及到内存空间的分配。
    
    */
    
        // test4  -- 补充实验
        const int s[10] = {1,2,3,4,5,6,7,8,9,10};
        int& s1 = const_cast<int&>(s[0]);
    
        s1 = 5;
        cout << "s[0] = " << s[0] << endl;
        cout << "s1 = " << s1 << endl;
       // 实测,这里的数组值可以被修改。看test5. 是推导出下面结论的步骤细节。
       // 先爆结论: 
       // const修饰栈上的数组,其数组元素是只读变量,而const修饰全局数据区的数组,其数组元素是常量。
       
    
    
        // test5  --- 对test4的结果不解,竟然能够修改const数组的元素值! 于是进行本test5,做增补实验。
    
        // test5 - PART1
        #if 1 
            const int f[10] = { 1,2,3,4,5,6,7,8,9,10 };
    
            // 下面使用指针或引用两种方法来测试,二选一即可。
    
            #if 1 // 使用指针来修改值
                int* pf = (int*)&f[0];
                *pf = 5;
                cout << "*pf = " << *pf << endl;
                // 实测,值还是被修改了,      
    
            #else // 使用引用来修改值
                int& f1 = (int&)f[0];
    
                f1 = 5;
                cout << "f[0] = " << f[0] << endl;
                cout << "f1 = " << f1 << endl;
                // 实测,值还是被修改了,
               
            #endif
    
            // 结论: 这里的const数组元素不是常量,是只读变量。
    
             // 提出疑惑:这里代码展示的const修饰数组,得不到常量数组元素???为什么呢? 继续实验, 继续向下看:
    
        #else  // test5 - PART2
           
            int& f1 = (int)f[0]; // 修改代码,这里使用了全局数组const int f[10]
    
            #if 1
                f1 = 5;     // 实测,运行期执行本条语句时发生异常,程序奔溃,提示写入访问权限冲突。
            #else
                f[0] = 8;   // 对const修饰的全局数组元素赋值,编译器也会报错。
            #endif
    
            cout << "f[0] = " << f[0] << endl;
            cout << "f1 = " << f1 << endl;
    
            // 结论: 根据编译期的报错提示 和运行期的程序异常时提示的“写入访问权限冲突”,
            //        我们可以得出:  const修饰全局数据区的数组,其数组元素是常量。
            //        C++里const修饰的数组和其存储区有关系! 
            //        而之前 test5 - PART1 之所以得到只读数组元素,是因为const修饰的数组存储区域在栈上。
    
        #endif
    
    
        // test 6  . 深究const_cast,对const_cast的使用进行打擦边球
        #if 1
            // tes6 -- PART1  
            //    百度百科的解释:
            /*
            const_cast,其主要作用是:修改类型的const或volatile属性。使用该运算方法可以返回一个指向非常量的指针(或引用)指向b1,就可以通过该指针(或引用)对它的数据成员任意改变。
            */
            //对const_cast的基本使用在test4内已经有演示,
    
            // !我们这里对const_cast的实验进行打擦边球: 不传入只读变量,传入常量。!
    
            const int const_var = 3; // 千万注意,这里的const_var变量虽然也是在main函数内部定义,但是不要觉得这是栈变量,这是常量,新手学习C++时,这是最基本的例子。
                                     // 不要把我上述讨论的const修饰的栈数组,和这里const修饰的单个变量搞混淆,要区分开。
                                     // 否则,看完我的文章,连基本的使用C++的const定义一个常量都不会了,那不是技术退步了。
    
            int& ref_const_var = const_cast<int&>(const_var);  
            // 实测编译没报错。
            // 结论: 
            // 参见上述个人小结,第一条小结:
            //1:对常量使用extern关键字或者是 & 操作符时,内存中会新生成一份该常量对应的变量。
    
            // 这个实验相当于又把本文test1的实验重新做了一遍,但是这次加深了理解。
    
        #else
            // const_cast的正确打开方式, 不给实验了,直接写中文
            
            // const_cast的常用场景:
            // 因为不能把一个const只读变量直接赋给一个非const变量,必须要使用const_cast进行转换,才能避免编译器报错。
    
            // const_cast的冷门场景:
            // 即上述打擦边球的场景,遇到这种场景,重在理解,参见tes6 -- PART1  ,或者, 上述个人小结的第一条小结。 
    
        #endif
    
    
        return 0;
    }

    个人小结:

    <1> 1:对常量使用extern关键字或者是&操作符时,内存中会新生成一份该常量对应的变量。 2:引用,是对某个内存空间冠以别名,对应的就是变量,变量才占有内存空间。而常量存储在符号表内,是没有地址的。 3:当对引用取地址,取到的地址是某个内存空间的地址。 4:引用的本质是别名,如果此时已经存在了内存空间,那么之后的所有引用都只是该内存空间的别名,并不会涉及到内存空间的分配。
    <2>
    const修饰栈上的数组,其数组元素是只读变量,而const修饰全局数据区的数组,其数组元素是常量。
    <3>
    const_cast的常用场景:
    因为不能把一个const只读变量直接赋给一个非const变量, 有时候却有这种赋值需求,那就必须要使用const_cast进行转换,避免编译器报错。


    最后语: C语言里的const修饰的都是只读变量,但是C++的const完全和C的意义不同,因为C++里存在真正的常量。所以需要重新学习。 本实验阐释了部分C++引用、对引用取地址、const、全局const数组、局部const数组等特性和相关涉及,但尚不完整。
    以const关键字为例,const还可以修饰类对象、成员函数等,也有相关注意事项,本文均未作展示。






    .
    /************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
  • 相关阅读:
    Java实现各种排序算法
    Memcached学习笔记
    S.O.L.I.D 原则
    设计模式之Bridge
    UML建模工具比较
    UML建模
    Ps经典实例教程3000例
    ps视频教程全集
    自己做到了吗?
    记事本开发Java代码注意要点
  • 原文地址:https://www.cnblogs.com/happybirthdaytoyou/p/13765563.html
Copyright © 2011-2022 走看看