# i、const 和 constexpr 的区别

提问:const 和 constexpr 有什么区别?

回答:
constconstexpr 都用于创建常量,但它们之间有一些重要的区别。

以下是它们的区别以及如何使用它们:

  1. const:
    • const 用于创建只读的常量变量,其值在运行时不能被修改。
    • const 变量必须在声明时初始化,并且可以在运行时计算其值。
    • const 变量可以用于任何数据类型,包括基本数据类型、自定义类、指针等。
    • const 变量的值在编译时或运行时(如果它是一个函数的局部变量)计算。
    • const 变量的值不一定需要在编译时确定,但一旦初始化后就不能更改。
    • const 变量的地址可以传递给函数,但函数不能修改该变量的值。

1
const int myConstVar = 42;

  • constexpr:
    • constexpr 用于创建编译时常量,其值在编译时已经确定,不能在运行时修改。
    • constexpr 变量必须在声明时使用常量表达式初始化,这意味着它的值必须在编译时就能确定,通常是通过常量或字面值来计算。
    • constexpr 变量通常用于基本数据类型,如整数、浮点数等,以及由它们构成的类型。
    • constexpr 变量的值在编译时确定,因此在运行时不存在任何计算开销。
    • constexpr 变量通常用于模板编程、数组大小、枚举、函数的参数等需要在编译时确定值的上下文中。
    • constexpr 变量的地址不能传递给函数,因为它们在运行时不存在。

1
constexpr int myConstexprVar = 42;

总结:

  • 使用 const 创建只读的运行时常量,适用于各种数据类型,值在运行时确定。
  • 使用 constexpr 创建编译时常量,适用于基本数据类型和由它们构成的类型,值在编译时确定。
  • constexpr 变量通常用于需要在编译时确定值的上下文,以提高性能和代码优化。
  • 选择使用哪种关键字取决于你的需求,如果可以在编译时确定值,则首选 constexpr

追问:为什么说 constexpr 变量在运行时不存在?

constexpr 变量在 C++ 中的一个核心概念是编译期常量。这意味着 constexpr 变量的值必须在编译时就能确定,并且这些变量的所有求值都发生在编译时,而不是运行时。因此,通常我们说 constexpr 变量在运行时不存在,这是因为它们的值和用途在编译时已经完全确定,编译器可能会将这些值内联到代码中,而不会真正分配内存存储它们

  1. 编译时常量
    当你声明一个 constexpr 变量时,编译器会在编译阶段计算它的值。与 const 变量不同, constexpr 的值在编译时已经是固定的,不需要等到运行时。

  2. 内存分配优化
    编译器可以对 constexpr 变量进行优化,可能不会在程序的内存中为其分配特定的地址。编译器会直接使用这些变量的值,并在必要的地方进行值替换内联,这意味着这些变量在实际运行的二进制程序中没有内存位置或指针,无法传递其地址。

  3. 编译时求值
    由于 constexpr 变量是在编译时求值的,因此它们的存在只是在编译器的上下文中,而不是在程序运行时的内存空间中。例如:

    1
    constexpr int x = 42;  // x 在编译时被计算为 42

    在这段代码中, x 在编译时已知是 42 ,编译器可以在整个程序中直接使用 42 ,而不是存储一个 x 变量的实际地址。

  4. 地址限制
    由于 constexpr 变量的这种编译时行为,它们的地址无法在运行时确定,因此你不能将 constexpr 变量的地址传递给一个要求运行时地址的函数。例如,以下代码是不允许的:

    1
    2
    constexpr int x = 42;
    int *p = &x; // 错误,不能获取 `constexpr` 变量的地址

    这是因为 constexpr 变量没有在运行时分配地址,它的值已经完全存在于编译时上下文中。

追问:宏定义也是编译时常量吗,它和 constexpr 相比有何优劣?

回答:
#define 宏定义是一种预处理指令,用于在代码中创建符号常量,但它与 constexpr 有一些重要的区别和优劣势:

#define 宏定义:

  • #define 定义的宏是文本替换在编译前进行,而不是创建真正的编译时常量。这意味着它们仅仅是在代码中的文本替换,不会被编译器视为独立的常量。
  • #define 宏没有类型,它们只是纯文本替换。这可能导致类型不一致或错误的使用。
  • 宏的值在编译前无法检查,因此可能会引入难以调试的错误。
  • 宏定义的作用域是整个文件,它们在定义点之后的整个文件中都有效。
  • 由于是文本替换,宏定义可以用于创建各种代码片段,包括函数、语句等。

constexpr 编译时常量:

  • constexpr 用于创建真正的编译时常量,它们在编译时已经确定。这意味着它们具有类型和值,可以进行类型检查和优化。
  • constexpr 变量必须具有明确的类型,并且其值必须在编译时可计算。
  • 编译器可以在编译时检查 constexpr 变量的值,因此可以捕获错误。
  • constexpr 变量的作用域是常规的变量作用域,可以在需要时限制其可见性。
  • constexpr 变量通常用于编写更安全、更可读和更可维护的代码,因为它们提供了类型检查和更好的错误检测。

总结:

  • #define 宏定义是文本替换,不是真正的编译时常量,可能引入类型不一致和难以调试的问题。
  • constexpr 是用于创建编译时常量的关键字,提供了类型检查、编译时错误检测和更好的代码优化。
  • 推荐在 C++ 中使用 constexpr 来创建常量,以提高代码的可维护性和可读性,并避免潜在的问题。

另外,宏定义的作用域限定在单个文件内,它们不会自动影响其他文件。如果你在一个文件中定义了一个宏,其他文件需要包含这个宏的定义才能访问它。通常,宏定义会放在头文件中,然后在多个文件中包含该头文件,以确保多个文件可以共享相同的宏定义。

# i、enum 枚举

C++ 中的枚举(enum)是一种用户自定义的数据类型,用于定义命名的整数常量。枚举提供了一种更可读、更可维护的方式来表示一组相关的命名常量。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

// 定义一个枚举类型
enum Color {
RED, // 默认值为 0
GREEN, // 默认值为 1
BLUE // 默认值为 2
};

int main() {
// 使用枚举类型
Color myColor = GREEN;
// 输出枚举值
std::cout << "Selected color: " << myColor << std::endl;
return 0;
}

注意,在 C++ 中,枚举类型( enum )的值是常量,一旦定义就不能被改变。枚举提供了一种创建命名整数常量集的方式,这些常量的值是固定的,不能在运行时更改

与宏定义 define 的区别:
枚举是具有明确定义类型的常量。枚举类型的值在编译时被检查,并且枚举类型是有限的。
枚举是由编译器处理的,可以提供更多的类型安全性和错误检查。

更新于

请我喝杯咖啡吧~

Rick 微信支付

微信支付

Rick 支付宝

支付宝