const 与 constexpr

一、问题:

遇到过该问题:在类中,与static关键字一起使用时, 未在类外进行再次声明,导致编译报错

c++ 复制代码
class A
{
  public:
    void txMsg(const int& x) const;
    void rxMsg(const int x);

    void func();
  private:

    static const int  m_data1 = 10; 
    static constexpr int m_data2 = 20;
    static inline const int m_data3 = 30; 
};

// 正确做法, 类外进行再次声明
const int A::m_data1; 
constexpr int A::m_data2; 

A::func()
{
  this->txMsg(m_data1); 
  this->rxMsg(m_data1);
  
  this->txMsg(m_data2); 
  this->rxMsg(m_data2);

  this->txMsg(m_data3); 
  this->rxMsg(m_data3);
}

//错误做法, 未进行再次声明

A::func()
{
  this->txMsg(m_data1);       // 编译报错
  this->rxMsg(m_data1);
  
  this->txMsg(m_data2);       // 编译报错
  this->rxMsg(m_data2);

  this->txMsg(m_data3); 
  this->rxMsg(m_data3);
}

原因就在于static关键字上,单一定义原则(ODR),当静态成员变量需要取地址 时,必须在类外进行再次声明,否则编译器会报错。

类型 类内初始化 是否需要类外再次声明
static const int 可以 如果要被取地址,需要
static const 非整型 不可以 需要
static constexpr 任意类型 可以 如果要被取地址,需要
static inline const 任意类型 可以 不需要

二、 const 与 constexpr的区别

回顾下内存区域:

内存区域 存储内容 生命周期 管理对象
代码区 存放函数体的二进制代码 程序运行期间 操作系统
全局区 存放全局变量和静态变量以及常量 程序运行期间 操作系统
栈区 存放函数的参数值,局部变量 函数运行期间 编译器自动分配释放
堆区 存放new,malloc分配的内存数据 程序运行期间 程序员手动释放,否则由操作系统回收
  • const:可以修饰多种变量,包括局部变量、全局变量、静态变量等;因此const变量的内存区域取决于它定义的位置

    • 局部变量:栈区
    c++ 复制代码
    void func()
    {
      // 局部变量,栈区
      const int local_var = 10;
    }
    • 局部变量:堆区
    c++ 复制代码
    void func()
    {
      // 堆区
      const int* heap_var = new const int(10);
    
      delete heap_var;
    }
    • 全局变量:全局区
c++ 复制代码
const int global_var = 10; // 全局区
  • 静态变量:全局区
c++ 复制代码
class A
{
 private:
   static const int static_var = 10; // 全局区
};

// 需要在类外初始化,否则编译器会报错,尤其是函数引用到该值时
const int A::static_var = 10; // 全局区
  • constexpr: 是 编译期 常量(还包含表达式 ),必须在编译期就能计算出来;但它可能会被编译期给优化成一个立即数(取决于编译器)

    • 全局变量:可能只在编译期存在
    c++ 复制代码
    constexpr int global_var = 10; 
    • 局部变量:可能被编译器优化成一个立即数
    c++ 复制代码
    void func()
    {
      constexpr int local_expr = 100;
    
      int array[local_expr]; 
    }
  • 常见场景:

    1. 对数组进行初始化:
    c++ 复制代码
    int main()
    {
      const int arr_size = 10;
    
      int arr[arr_size];        // 报错,因为arr_size在编译期无法确定
                                // 如果想要const修饰的变量通过,那么得让它分配得内存,在编译期就能通过
    
      constexpr int arr_size_expr = 10;
      int arr[arr_size_expr]; // 正确
    
      return 0;
    }
    1. switch的case标签:
    c++ 复制代码
    const int red = 1;
    
    switch(color){
      case red:     //  报错,因为red在编译期无法确定
                    //  即便加上 sattic关键字,也不行
    
      case 1:       //  正确
        // ...
    }
    
    
    constexpr int red_expr = 1;
    switch(color){
      case red_expr: // 正确
        // ...
    }
    1. 替换宏定义:
    c++ 复制代码
    #define squre(x) { return x * x; }    //不推荐
    
    constexpr int squre_expr(int x) { return x * x; } // 推荐
    float array[squre_expr(3)]; // 正确

三、小结:

  • constexpr是C++11引入的,用于定义编译期常量。它不仅可以修饰变量,还可以修饰表达式,并且要求该表达式在编译时就能计算出来。相比之下,const关键字主要用于修饰变量,并且在运行时也可能存在(取决于变量的存储位置)。
  • 大部分场景推荐使用constexpr,因为它提供了更严格的约束和更好的性能。
相关推荐
wenjie学长5 小时前
[UE学习笔记]—划时代意义的两大功能—lumen和Nanite
笔记·学习·ue·三维数字化
好奇龙猫5 小时前
日语学习-日语知识点小记-构建基础-JLPT-N3阶段-二阶段(6):文法運用
学习
文火冰糖的硅基工坊5 小时前
[人工智能-大模型-43]:模型层技术 - 强化学学习:学习的目标、收敛条件、评估依据、应用到的模型、应用场景 - 通俗易懂。
人工智能·学习
枫叶丹46 小时前
【Qt开发】多元素类控件(三)-> QTreeWidget
开发语言·数据库·c++·qt
hansang_IR6 小时前
【题解】P2217 [HAOI2007] 分割矩阵 [记忆化搜索]
c++·数学·算法·记忆化搜索·深搜
奋斗的牛马6 小时前
FPGA—ZYNQ学习Helloward(二)
单片机·嵌入式硬件·学习·fpga开发
qq_386322697 小时前
华为网路设备学习-34(BGP协议 九)BGP路由 选路规则二
服务器·学习·华为
尘似鹤7 小时前
设计一个状态机
学习·状态模式·嵌入式软件
Voyager_48 小时前
算法学习记录03——二叉树学习笔记:从两道题看透后序位置的关键作用
笔记·学习·算法