zoukankan      html  css  js  c++  java
  • c/c++的const说明符——深入挖掘



    const变量的文件作用域

    以前从来没有注意到的一个知识点: const 修饰的对象默认只有当前文件中有效。这就表明了:

    • 在不同的.cpp文件内可以定义相同名称的const 对象。
    • 如果要使const 我修饰的变量具有全局使用域,在定义该变量时,需要加上extern 关键字。

    不使用extern关键字定义const变量

    在1.cpp文件中,定义如下:

    const int a = 100;
    

    在2.cpp文件中,定义如下:

    extern const int a;
    int main() {
    	cout << a << endl;
    	return 0;
    }
    

    我们进行编译, 会报链接错误,原因是:在2.cpp文件找不到变量a的定义。

    使用extern关键字定义const 变量

    在1.cpp文件中,定义如下:

    extern const int a = 100;
    

    在2.cpp文件中,定义如下:

    extern const int a;
    int main() {
    	cout << a << endl;
    	return 0;
    }
    

    编译正常,代码运行正常:

    进一步分析原因

    我猜测当定义一个const变量时,使用与不使用extern关键字时,生成的变量的符号不相同。下面进行验证。

    • 第一步:新建一个1.cpp文件,定义如下:

      const int value1 = 100;
      extern const int value2 = 100;
      int value3 = 100;
      extern int value4 = 100;
      
    • 第二步, 编译成.o文件。(报了一个编译警告,不管它)

    • 第三步:使用readelf工具看一下.o文件的符号表

    我们看到:当定义一个const变量时,如果不使用extern 关键字进行修饰,默认生成的变量的符号为local 属性,而使用extern 关键字时,生成的符号为global属性。 对应local属性的符号,在链接的时候是找不到的。所以,const 对象默认只在文件内有效。

    修改const修饰的变量会发生什么

    修改const修饰的全局变量

    看如下代码:

      1 #include <iostream>
      2 using namespace std;
      3 
      4 const double a = 10.5;
      5 
      6 int main() {
      7     double* p = const_cast<double*>(&a);
      8     *p = 110.5;
      9     return 0;
     10 }
    
    

    编译可过,运行时,段错误。

    原因是全局的const变量分配到了只读内存区。可以写如下代码验证一下:

      1 #include <iostream>
      2 using namespace std;
      3 
      4 const int a = 10.5;
      5 const int b = 10.5;
      6 
      7 int c = 100;
      8 int d = 100;
      9 
     10 int main() {
     11     cout << &a << endl;
     12     cout << &b << endl;
     13     cout << &c << endl;
     14     cout << &d << endl;
     15     return 0;
     16 }
    

    输出内容如下所示。内存地址差了很多的。

    修改const修饰的局部变量

    代码如下:

    
      1 #include <iostream>
      2 using namespace std;
      3 
      4 int main() {
      5     const double a = 10.5;
      6     double* p = const_cast<double*>(&a);
      7     *p = 20.5;
      8     cout << a << endl;
      9     cout << *p << endl;
     10     return 0;
     11 }
    
    

    编译后,输出如下图:

    输出符合你的预期没? 继续执行下面代码:

     1 #include <iostream>
      2 using namespace std;
      3 
      4 int main() {
      5     volatile const double a = 10.5;
      6     double* p = const_cast<double*>(&a);
      7     *p = 20.5;
      8     cout << a << endl;
      9     cout << *p << endl;
     10     return 0;
     11 }
    

    编译后,输出为如下图:

    原因:编译器对const变量进行了优化,读取它的值时,直接从寄存器里拿。当加上volatile修饰后,编译器指示每次从内存中读取。

    c++的const属性为bitwise

    什么是bitwise-const

    为了说明什么是bitwise-const, 直接上代码, 让你惊讶一下

      1 #include <iostream>
      2 
      3 using namespace std;
      4 
      5 struct Info{
      6     int& a;
      7     int* p;
      8 };
      9 
     10 void Modify(const Info& input) {
     11     input.a += 100;
     12     *input.p += 100;
     13 }
     14 
     15 int main() {
     16     int num1 = 100;
     17     int num2 = 100;
     18     Info input{num1, &num2};
     19     cout << input.a << endl;
     20     cout << *input.p << endl;
     21 
     22     Modify(input);
     23 
     24     cout << input.a << endl;
     25     cout << *input.p << endl;
     26     return 0;
     27 }
    
    

    编译成功, 输出如下:

    上面的结果,是否有点不习惯呢? bitwise-const, 就是修饰的对象的物理BIT位不可以修改。但是呢,如果结构体的成员为指针或者引用, 你是限制不住通过该const变量修改指针指向的变量的值或者引用的值。

    如何取某一个成员变量的const属性

    使用mutable修饰符可以取消一个const修饰的结构体或类对象内某一个变量的const属性。 mutable变量在类内经常使用,目的是可以让const修饰的成员函数修改成员变量。

    使用mutable修改前,代码如下,编译错误。

     1 #include <iostream>
      2 
      3 using namespace std;
      4 
      5 struct Info{
      6     int a;
      7     int b;
      8 };
      9 
     10 int main() {
     11     const Info data{10, 20};
     12     data.a = 100;
     13     data.b = 100;
     14     cout << data.a << " " << data.b << endl;
     15     return 0;
     16 }
    
    

    使用mutable修改后,代码如下,编译OK,运行OK。

     1 #include <iostream>
      2 
      3 using namespace std;
      4 
      5 struct Info{
      6     mutable int a;
      7     mutable int b;
      8 };
      9 
     10 int main() {
     11     const Info data{10, 20};
     12     data.a = 100;
     13     data.b = 100;
     14     cout << data.a << " " << data.b << endl;
     15     return 0;
     16 }
    

  • 相关阅读:
    【Android游戏开发之八】游戏中添加音频详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!
    【Android游戏开发之九】(细节处理)触屏事件中的Bug解决方案以及禁止横屏和竖屏切换!
    【Android游戏开发之七】(游戏开发中需要的样式)再次剖析游戏开发中对SurfaceView中添加组件方案!
    前端要给力之:URL应该有多长?
    【Android游戏开发之三】剖析 SurfaceView ! Callback以及SurfaceHolder!!
    charactersFound方法中的陷阱
    前端要给力之:分解对象构造过程new()
    结合UIImageView实现图片的移动和缩放
    【Android游戏开发之一】设置全屏以及绘画简单的图形
    扩展BaseAdapter实现在ListView中浏览文件
  • 原文地址:https://www.cnblogs.com/yinheyi/p/14729820.html
Copyright © 2011-2022 走看看