• 【C++】C++中const与constexpr的比较


    先说结论
    相同点:const和consexpr都是用来定义常量的。
    不同点:const声明的常量,初始值引用的对象不一定是一个常量;constexpr声明的常量,初始值一定是常量表达式。

    constexpr是c++11标准添加的关键字。

    之所以说const声明的常量,初始值不一定是一个常量,主要是从引用和指针的角度出发的。如果初始化const时候,指定一个固定的字面值常量,那么它引用的肯定是常量。

    const int i = 10;
    constexpr int j = 20;

    这种情况下,i和j都是常量,而且它们引用的也是一个常量(因为是固定的字面值)。那么如果它们引用的不是固定的字面值,而是指针和引用呢?接下来笔者将从引用和指针的角度出发,解释const和constexpr的区别:

    const与引用

    通过如下语法可以声明一个常量int类型引用:

    const int &v;

    注意这里的v是一个常量int引用,也就是说,v是肯定是一个int类型的常量引用(值不能改变),应该给它赋值常量int类型,如果我们给它赋值变量int类型会怎么样呢?看下面的案例。
    例如:

    #include <iostream>
    using namespace std;
    int main(){
        int a = 20;
        const int &b = a;//引用a,常量int引用b引用了非常量inta
        cout << "a = " << a << ", b = " << b << endl;
        a = 10;//可以通过a改变变量的值
        cout << "a = " << a << ", b = " << b << endl;
        //b = 20;//出错,不可以通过b改变变量的值,因为b一个常量引用,所以不能通过b去改变。
        return 0;
    }

    结果:

    a = 20, b = 20
    a = 10, b = 10

    上面的案例中,a是一个变量,b是一个常量引用。a变量的值不能通过b来改变,但是可以通过a来改变,因为a不是常量,b是常量引用(b认为自己引用的是一个常量,实际却不是)。

    const与指针

    可以通过如下的方式,来声明一个常量指针。

    int *const p;

    p首先是一个常量,然后再是一个指针,并且这个指针指向一个int类型。

    下面的案例

    #include <iostream>
    using namespace std;
    int main(){
        int i = 10;
        int *const p = &i;//指向非常量的常量指针p,指向了非常量i
        cout << "i = " << i << ",*p = " << *p << endl;
        i = 20;
        cout << "i = " << i << ",*p = " << *p << endl;
        *p = 30;
        cout << "i = " << i << ",*p = " << *p << endl;
        int j = 0;
        //p = &j;//出错
        return 0;
    }

    输出

    i = 10,*p = 10
    i = 20,*p = 20
    i = 30,*p = 30

    上面的案例中p是一个常量类型的指针,并且指向一个非常量int对象。由于p是指针,所以*p解地址后实际上是变量i,所以可以通过*p改变变量的值。但是p = &j语句,会改变p变量中存储的地址(初始化时存储的是变量i的地址),由于p是常量,所以p中的值是不能改变,因此p = &j会报错。

    当然可以这样定义

    const int *const p;

    这样的话,p是一个常量指针,并且指向常量int类型。
    例如:

    #include <iostream>
    using namespace std;
    int main(){
        int i = 10;
        const int *const p = &i;//指向常量int的常量指针p,指向了非常量i
        cout << "i = " << i << ",*p = " << *p << endl;
        i = 20;
        cout << "i = " << i << ",*p = " << *p << endl;
        //*p = 30;//出错
        int j = 0;
        //p = &j;//出错
        return 0;
    }

    输出结果:

    i = 10,*p = 10
    i = 20,*p = 20

    虽然常量指针p应该指向一个常量int类型,但是笔者给它一个非常量int类型的地址,这样一来,p会认为它指向的是一个常量int类型,所以当通过*p = 30改变它的值时,不能通过。但是通过i依然可以修改。

    小结:
    常量引用可以引用非常量,非常用引用不能引用常用。
    指向常量的指针可以指向一个非常量,指向非常量的指针不能指向常量。

    从逻辑上可以这样理解:因为非常量具有可读可写的功能,常量只有可读的功能;当常量引用非常量时,常量只期望可以读数据,非常量不仅提供了读数据,而且非常量还可以写数据(修改),因此常量引用非常量可以满足常量的需求,可以通过;返过来,常量不能够满足非常量的需求,所以不能通过。

    int a = 10;
    const int &b = a;//正确,b只需要读数据,a可以读数据,所以可以通过。
    
    const int c = 10;
    int &d = c;//错误,d需要读数据和写数据,c只能提供读数据,所以不通过。
    
    int e = 10;
    const int *f = &e;//正确,*f只需要能够读数据,e可以读数据,所以可以通过。
    
    const int g = 10;
    int *h = &g;//错误,*h需要读数据和写数据,g只能提供读数据,所以不通过。

    上面笔者总结的规律还有一些需要补充,在不改变const对象的操作中还有一种是初始化,如果一个利用对象去初始化另外一个对象(引用和指针除外,这里主要是指拷贝),则他们是不是const都无关紧要:

    int i = 42;
    const int ci = i; // 正确:i的值被拷贝给了ci
    int j = ci; //正确 : ci的值被拷贝给了j

    尽管ci是const类型,j是int类型。ci的常量特征仅仅在执行改变ci的操作时才会发挥作用,当用ci初始化j时,更本无需在意ci是不是一个常量。拷贝一个对象的值不会改变它,一旦拷贝完成,新的对象和原来的对象就没什么关系了。

    constexpr

    在上面的说过了const的特点,可以得出,当const变量引用或指向某个变量时,被引用或指向的变量是不能确定是否是一个常量的。
    C++11标准提供了constexpr关键字,被constexpr修饰的变量的初始值必须是常量表达式,也就是说,被constexpr修饰的变量,肯定是常量,而且引用常量表达式。

    constexpr int m = 10;//20是常量表达式
    constexpr int n = m + 1;//m+1是一个常量表达式

    //* & 都不属于常量表达式
    constexpr const int *p = &m;//错误
    constexpr const int &r = m;//错误


  • 相关阅读:
    改变对象的字符串提示
    perl 和 python中的回调函数
    shiro权限验证标签
    user_tables 的信息依赖于统计信息
    centos jdk 1.7升级到1.8后显示还是1.7
    7.1 可接受任意数量参数的函数:
    IntelliJ IDEA中怎么查看文件中所有方法(类似eclipse里面的outline)
    Intellij IDEA 代码格式化与eclipse保持风格一致
    jquery-1.9.1.min.js:69 Uncaught TypeError: Illegal invocation
    python json模块
  • 原文地址:https://www.cnblogs.com/HDK2016/p/10244389.html
Copyright © 2020-2023  润新知