C++ const和constexpr详解

C++ const和constexpr详解使用变量的好处是当我们觉得变量值不合适时可以随时调整 但是这也会带来弊端 比如容易无意间改变了它的值 为了避免这种情况 可以用关键字 const 对变量的类型加以限定 但是使用 const 进行声明时 我们需要人为的验证赋给 const 对象的初始值是不是常量表达是 在复杂系统中 有时候很难分辨 而使用 constexpr 进行声明时 可以由编译器来验证变量是不是常量表达式

const 限定符

使用变量的好处是当我们觉得变量值不合适时可以随时调整,但是这也会带来弊端,比如容易无意间改变了它的值,为了避免这种情况,可以用关键字const对变量的类型加以限定。

初始化

const限定符定义的对象必须初始化。
在指针和引用这篇博客中,我曾介绍了对于const 指针和const 引用的初始化方式。
其实const类型的对象能执行大部分非const类型对象的操作,只有对const类型的对象执行改变其内容的操作时,该限定符才起作用,不仅如此,如果利用一个对象去初始化另外一个对象,则它们是不是const都无所谓




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

拷贝一个对象的值并不会改变被拷贝对象的值,一旦拷贝完成,新的对象和被拷贝对象就是两个独立的对象

const修饰指针

顶层const:指针本身是个常量
低层const:指针所指向的对象是个常量或者引用绑定的对象是一个常量

  • 对于对象类型为基本数据类型情况,其const都是顶层const
class A { 
    public: int val; }; int main() { 
    const int i = 10; //不能改变i的值,这是一个顶层const const double d = 1.2; //不能改变d的值,这是一个顶层const const char c = 'a'; //不能改变c的值,这是一个顶层const const A object_A; //不能改变object_A的值,这是一个顶层const return 0; } 
  • 对于对象类型是指针的情况,其对象即可以是顶层const,也可以是底层const
int i = 10; int j = 20; const int *p1 = &i; //允许改变指针p1的值,即可以改变其指向,但不允许改变i的值,这是一个底层const p1 = &j; //使p1从指向i改为指向j cout << *p1 << endl; //输出j的值,20 int* const p2 = &i; //允许改变i的值,但是不允许改变指针p2的值,这是一个顶层const *p2 = 15; //改变指针p2指向的对象的值,即i cout << *p2 << endl; //输出i的值,15 const int *const p3 = &i;//不允许改变i的值,也不允许改变指针p3的值,右边的const顶层const,左边的const底层const p3 = &j; //错误,意图改变p3的值 *p3 = 10; //错误,意图改变指针p3指向对象的值 const int&r = i; //不允许改变引用r所绑定对象的值,这是一个底层const r = 10; //错误,意图改变引用r所绑定对象的值 

很多时候我们分不清const到底是作用于指针本身还是指针所指向的对象,基于此,我们可以通过看const右边是变量名还是数据类型来判别,注意:int const 和const int是一样的
举例:
上面的p1,由于const右边是数据类型int,所以该const作用于指针所指向的对象
上面的p2,由于const右边是指针p2,所以该const作用于指针本身






  • 由于指针时对象,而引用不是,所以声明引用的const都是底层const

const参数传递

值传递

  • 对于内置的基本数据类型,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
    const void func(const int i) { 
          cout << i << endl; i++;//错误,i的值不能修改 } 

传递指针或引用

  • 对于自定义类型的参数,需要临时对象复制实参值,而对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们一般采取 const 外加引用传递的方法。并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。
    class Test { 
          public: int val; }; const void func(const Test&object) { 
          } 

    除了const引用,使用const传递指针,即指针传递,也可以防止对象被意外篡改。

    const void func1(const int *i) { 
          cout << *i << endl; int j = 20; i = &j;//正确 *i = 10;//错误,不能改变指针所指向的对象 } const int func2(int*const i) { 
          cout << *i << endl; int j = 20; i = &j;//错误,不能改变指针的指向 *i = 10;//正确 } const int func3(const int*const i) { 
          cout << *i << endl; int j = 20; i = &j;//错误,不能改变指针的指向 *i = 10;//错误,不能改变指针所指向的对象 } 

    对于引用传递指针传递的区别,在指针和引用这篇博客中有详细介绍

const函数返回

  • const 修饰内置类型的返回值,修饰与不修饰返回值作用一样。
  • const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
  • const 修饰返回的指针或者引用,根据返回的是顶层const还是底层const来进行相应的操作

const成员函数

class Test{ 
    public: int val; int TestFunc()const{ 
    return val; } }; void func(const Test& object){ 
    cout << object.TestFunc(); } 

如果 TestFunc() 去掉 const 修饰,即使在函数func中没有改变object的值,编译器也认为函数会改变对象的值从而报错,所以对于不需要改变对象内容的函数我们尽量都作为 const 成员函数。

mutable关键字

class Test{ 
    public: int m; mutable int n; void TestFunc()const{ 
    m++;//错误 n++;//正确 } }; 

constexpr 限定符

常量表达式

常量表达式:指值不会改变并且在编译过程就能得到结果的表达式;字面值、用常量表达式初始化const对象也是常量表达式。
字面值类型:算术类型、引用和指针都属于字面值类型,自定义类、IO库,string类型则不属于字面值类型,不能被定义成constexpr;

const int i = 10; //字面值是常量表达式 const int j = i+1; //j是常量表达式 int k = i; //k不是const对象,所以k不是是常量表达式 const int m = get_val(); //m的值直到运行时才能获得,所以m不是常量表达是 

constexpr变量

从上面我们可以看出,使用const进行声明时,我们需要人为的验证赋给const对象的初始值是不是常量表达是,在复杂系统中,有时候很难分辨,而使用constexpr进行声明时,可以由编译器来验证变量是不是常量表达式

constexpr函数

constexpr函数:指能用于常量表达式的函数,其定义方式和普通函数类型;
定义规则

  • 函数的返回类型及所有形参类型都是字面值类型
  • 函数体中只有一条return 语句
constexpr int func(int n) { 
    return n; } int main() { 
    int n=10; const int m=10; constexpr int i = func1(10);//正确,i是一个常量表达式 constexpr int j = func1(n);//错误,n不是字面值 constexpr int k = func1(m+1);//正确,k是一个常量表达式 return 0; } 

const和constexpr区别

  • 对于修饰对象来说,const并未区分出编译期常量和运行期常量,constexpr限定在了编译期常量
  • 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
const int*p1=nullptr; //p1是一个指向常量的指针 constexpr int*p2=nullptr; //p2是一个指向整数的常量指针 constexpr const int*p3=nullptr; //p3是一个指向常量的常量指针 

参考

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228346.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午7:14
下一篇 2026年3月16日 下午7:15


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号