C++深度解析(2)—C/C++中的const分析

1.C语言中的const

1.1 const只读变量

  • const修饰的变量是只读的,本质还是变量
  • const修饰的局部变量在栈上分配空间
  • const修饰的全局变量在全局数据区分配空间
  • const只在编译期有用,在运行期无用
  • const修饰的变量不是真的变量,它只是告诉编译器该变量不能出现在赋值符号的左边

1.2 const全局变量的分歧

  • 在现代C语言编译器中,修改const全局变量将导致程序崩溃
  • 标准C语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变。
#include <stdio.h>  
  
const int g_cc = 2;  
  
int main()  
{  
    const int cc = 1;  
      
    int *p = (int *)&cc;  
      
    printf("cc = %d\n", cc);  
      
    *p = 3;  
      
    printf("cc = %d\n", cc);  
      
    p = (int *)&g_cc;  
      
    printf("g_cc = %d\n", g_cc);  
      
    *p = 4;  
      
    printf("g_cc = %d\n", g_cc);  
      
    return 0;  
} 
  • gcc、VS2017、BCC(标准C)编译结果:

1.3 const的本质

  • C语言中的const使得变量具有只读属性
  • 现代C编译器中的const将具有全局生命周期变量存储于只读存储区(所以gcc、vs2017都会出错
  • const不能定义真正意义上的常量
#include <stdio.h>  
  
const int g_array[5] = {0};  
  
void modify(int* p, int v)  
{  
    *p = v;  
}  
  
int main()  
{  
    int const i = 0;  
    const static int j = 0;  
    int const array[5] = {0};  
      
    modify((int *)&i, 1);           // ok  
    modify((int *)&j, 2);           // error  
    modify((int *)&array[0], 3);    // ok  
    modify((int *)&g_array[0], 4);  // error  
      
    printf("i = %d\n", i);  
    printf("j = %d\n", j);  
    printf("array[0] = %d\n", array[0]);  
    printf("g_array[0] = %d\n", g_array[0]);  
      
    return 0;  
}  
  • gcc、BCC(标准C)编译结果:

1.6 const修饰函数参数和返回值

  • const修饰函数参数表示在函数体内不希望改变参数的值
  • const修饰函数返回值表示返回值不可改变,多用于返回指针的情形
  • 小贴士:C语言中的字符串字面量存储于只读存储区,在程序中需要使用  const char* 指针
#include <stdio.h>  
  
const char* f(const int i)  
{  
  //  i = 5;  //error: assignment of read-only parameter ‘i’
      
    return "Test";  
}  
  
int main()  
{  
    const char* pc = f(0);  
      
    printf("%s\n", pc);  
      
   // pc[6] = '_';  //test.c:16:11: error: assignment of read-only location ‘*(pc + 6u)’
      
    printf("%s\n", pc);  
      
    return 0;  
}  
  • 运行结果

1.7 小结

  • const使得变量具有只读属性
  • const不能定义真正意义上的常量
  • const将具有全局生命期的变量存储于只读存储区

2 C++中的const

2.1 编程实验 test.cpp

#include <stdio.h>  
  
int main()  
{  
    const int c = 0;  
    int *p = (int *)&c;  
      
    printf("Begin...\n");  
      
    *p = 5;  
      
    printf("c = %d\n", c); 
     
    printf("*p = %d\n", *p);
  
    printf("End...\n");  
      
    return 0;  
}  
  • vs2017运行结果:

2.2 C++中的const

  • C++在C的基础上对const进行了进化处
  • 当碰见const声明时在符号表中放入常量
  • 编译过程中若发现使用常量则直接以符号表中的值替换
  • 编译过程中若发现下述情况则给对应的常量分配空间
    • 1. 对const常量使用了extern
    • 2. 对const常量使用&操作符
  • 注意:C++编译器虽然可能为const常量分配空间,但不会使用其存储空间的值。         

  • 当遇到&c时,为其分配空间,分配空间只是为了兼容C语言
  • C语言中的const变量:C语言中const变量是只读变量,会分配存储空间
  • C++中的const常量:可能分配存储空间:
    • 当const常量为全局,并且需要在其它文件中使用
    • 当使用&操作符对const常量取地址
  • C++中的const常量类似于宏定义:const int c = 5;≈  #define c 5
  • C++中的const常量与宏定义不同
    • const常量由编译器处理,编译器对const常量进行类型检查和作用域检查
    • 宏定义由预处理器处理,单纯的文本替换
#include <stdio.h>

void f()
{
    #define a 3
    const int b = 4;  // b的作用域仅在f函数内
}

void g()
{
    printf("a = %d\n", a);
    //printf("b = %d\n", b);   // error: 'b' was not declared in this scope 
}

int main(int argc, char *argv[])
{
    f();
    g();
    
    printf("Press enter to continue ...");
   
	getchar();
    
    return 0;
}

2.3 const常量的判别准则 

  • 只有用字面量初始化的const常量才会进入符号表 
  • 使用其它变量初始化的const常量仍然是只读变量
  • 被volatile修饰的const常量不会进入符号表 
  • 在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理。
  • const引用的类型与初始化变量的类型 
    • 相同:初始化变量成为只读变量 
    • 不同:生成—个新的只读变量 
#include <stdio.h>  
  
int main()  
{  
    const int x = 1;  
    const int &rx = x;  
      
    int &nrx = const_cast<int &>(rx);  
      
    nrx = 5;  
      
    printf("x = %d\n", x);  
    printf("rx = %d\n", rx);  
    printf("nrx = %d\n", nrx);  
    printf("&x = %p\n", &x);  
    printf("&rx = %p\n", &rx);  
    printf("&nrx = %p\n", &nrx);  
      
    volatile const int y = 2;  
    int* p = const_cast<int *>(&y);  
      
    *p = 6;  
      
    printf("y = %d\n", y);  
    printf("p = %p\n", p);  
      
    const int z = y;  
      
    p = const_cast<int *>(&z);  
      
    *p = 7;  
      
    printf("z = %d\n", z);  
    printf("p = %p\n", p);  
      
    char c = 'c';  
    char &rc = c;  
    const int &trc = c;  //类型不同,新的只读变量
      
    rc = 'a';  
      
    printf("c = %c\n", c);  
    printf("rc = %c\n", rc);  
    printf("trc = %c\n", trc);  
      
    return 0;  
}  
  • 运行结果

2.4 小结

  • 与C语言不同,C++中的const不是只读变量
  • C++中的const是一个真正意义上的常量
  • C++编译器可能会为const常量分配空间
  • C++完全兼容C语言中const常量的语法特性
  • 指针是—个变量 
  • 引用是一个变量的新名字 
  • const引用能够生成新的只读变量 
  • 在编译器内部使用指针常量实现“引用” 
  •  编译时不能直接确定初始值的const标识符都是只读变量

3. mutable关键字

  • mutable是为了突破const函数的限制而设计的 
  • mutable成员变量将永远处于可改变的状态 
  • mutable在实际的项目开发中被严禁滥用
  • mutable的深入分析 
    • mutable成员变量破坏了只读对象的内部状态 
    • const成员函数保证只读对象的状态不变性 
    • mutable成员变量的出现无法保证状态不变性
  • 实例分析
  • 统计对象中某个成员变量的访问次数 
#include <iostream>  
  
using namespace std;  
  
class Test  
{  
    int m_value;  
    mutable int m_count;  
public:  
    Test(int value = 0)  
    {  
        m_value = value;  
        m_count = 0;  
    }  
      
    int getValue() const  
    {  
        m_count++;  //const成员函数,内部不能直接改变成员变量值  
        return m_value;  
    }  
      
    void setValue(int value)  
    {  
        m_count++;   
        m_value = value;  
    }  
      
    int getCount() const  
    {  
        return m_count;   
    }  
  
};  
  
int main(int argc, char *argv[])  
{  
    Test t;  
      
    t.setValue(100);  
      
    cout << "t.m_value = " << t.getValue() << endl;  
    cout << "t.m_count = " << t.getCount() << endl;  
      
    const Test ct(200);  
      
    cout << "ct.m_value = " << ct.getValue() << endl;  
    cout << "ct.m_count = " << ct.getCount() << endl;  
      
    return 0;  
}  
  • 运行结果

  • 更好的解决方案
#include <iostream>  
#include <string>  
  
using namespace std;  
  
class Test  
{  
    int m_value;  
    int * const m_pCount;  
    /* mutable int m_count; */  
public:  
    Test(int value = 0) : m_pCount(new int(0))  
    {  
        m_value = value;  
        /* m_count = 0; */  
    }  
      
    int getValue() const  
    {  
        /* m_count++; */    //只读对象内部不能直接改变  
        *m_pCount = *m_pCount + 1;  
        return m_value;  
    }  
      
    void setValue(int value)  
    {  
        /* m_count++; */  
        *m_pCount = *m_pCount + 1;  
        m_value = value;  
    }  
      
    int getCount() const  
    {  
        /* return m_count; */  
        return *m_pCount;  
    }  
  
    ~Test()  
    {  
        delete m_pCount;  
    }  
};  
  
int main(int argc, char *argv[])  
{  
    Test t;  
      
    t.setValue(100);  
      
    cout << "t.m_value = " << t.getValue() << endl;  
    cout << "t.m_count = " << t.getCount() << endl;  
      
    const Test ct(200);  
      
    cout << "ct.m_value = " << ct.getValue() << endl;  
    cout << "ct.m_count = " << ct.getCount() << endl;  
      
    return 0;  
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值