在C++中,常量是指其数值或数值引用在程序执行期间不能被改变的变量。常量可以在程序中用来定义一些固定不变的值,如数学常数、固定参数等。在C++中,有两种主要类型的常量:const和constexpr。
const常量概念:
constexpr常量概念:
const关键字的作用:
constexpr关键字的作用:
两种方式来声明和定义const变量:
通过变量声明和定义分开的方式(用于在多个文件之间共享const变量):
// 声明const变量
extern const int myConst; // 声明一个外部链接的const变量
// 在另一个文件中定义const变量
const int myConst = 10;
直接声明和定义const变量:
// 声明并定义const变量
const int myConst = 10;
const 成员函数表示该函数不会修改对象的成员变量,这样的函数可以被 const 对象调用。
示例:
class MyClass {
public:
void regularFunction() {
// 正常的成员函数定义
_val = 1;
}
void constFunction() const {
// const 成员函数定义
// 在 const 成员函数中不能修改对象的成员变量
}
private:
int _val;
};
一旦将一个成员函数声明为 const,那么这个函数就不能对对象的成员变量进行任何修改操作。
const修饰符还可以用于引用和指针类型,以表明它们指向的数据是常量,不能被修改。
const引用:可以通过在引用声明中添加const修饰符来创建const引用,如下所示:
int x = 10;
const int& ref = x; // 创建一个指向常量的引用
const指针:同样地,在指针声明中添加const修饰符可以创建const指针,如下所示:
int y = 20;
const int* ptr = &y; // 创建一个指向常量的指针
还可以这样声明指向常量的指针:
int z = 30;
int* const ptr2 = &z; // 创建一个常量指针
const在编译时的作用:
如果一个变量被声明为const,则编译器可以将其视为一个常量表达式,从而在编译时进行优化。即编译器可以在编译期间进行常量折叠和替换,而不必在运行时再去计算这个值。
编译时类型检查:使用const可以将变量声明为只读,确保在编译时不会发生意外的修改。如果尝试在const变量上进行写操作,编译器会报错。
const修饰指针和引用时,可以指示指针和引用所指向的内容为常量,这样可以在编译时进行一定的错误检查。
const的限制:
常量必须进行初始化:一个变量被声明为const,它必须在声明时进行初始化,否则编译器会报错。这是因为const在C++中被设计为一种“一旦赋值,不可更改”的特性。
对于const指针或引用来说,虽然它们指向的值是不可修改的,但是它们本身并不是常量。因此,可以通过改变指针或引用的指向来改变它们指向的值,这个要非常小心。
const的适用场景:
固定不变的数值或变量时,可以将其声明为const,以确保这些值在程序的执行过程中不会被修改。
将函数的参数声明为const可以确保函数内部不会对参数进行修改,同时向调用者明确表明函数的行为。
在编译时进行常量折叠和替换。
constexpr关键字用于声明变量和函数为编译时常量表达式。它可以用于在编译时求值,以及在运行时提供常量值。
constexpr变量的语法:
constexpr type variable_name = value;
其中,type
表示变量的类型,variable_name
表示变量的名称,value
表示变量的初始值。重点注意的是,value
必须是一个常量表达式,必须在编译时就可以确定。
示例:
constexpr int width = 10; // 声明一个constexpr变量
constexpr函数的语法:
constexpr return_type function_name(parameters) {
// 函数体
return value;
}
return_type
表示函数返回值的类型,function_name
表示函数的名称,parameters
表示函数的参数列表,value
表示函数的返回值。重点注意的是,constexpr函数的参数和返回值类型必须是能够在编译期间确定的常量表达式。
示例:
constexpr int square(int x) {
return x * x;
}
注意:在C++14之前,constexpr函数的函数体通常被要求是非常简单的,例如只有一条return语句。但是在C++14中放宽了这一限制,允许constexpr函数包含有限的控制流、局部变量和循环。
constexpr变量的特点:
示例:
constexpr int width = 10; // 定义一个编译时常量
constexpr int area = width * width; // 定义一个编译时常量表达式
constexpr函数的特点:
示例:
constexpr int square(int x) {
return x * x;
}
前面说了这么多constexpr在编译时的注意事项,这里再总结一下constexpr在编译时的作用:
常量表达式的求值:constexpr关键字告诉编译器,变量或函数的值可以在编译时求值。即编译器会在编译阶段计算constexpr变量和函数的值,而不是在运行时。这可以在一定程度上提高程序的性能,因为编译时求值的常量表达式可以避免在运行时进行重复的计算。
编译时的优化:使用constexpr可以让编译器在编译时对代码进行更多的优化。例如,编译器可以在编译期间内联constexpr函数,消除不必要的计算,以及在一些情况下将表达式简化为常量。这可以提高程序的执行效率,并减少运行时的开销。
constexpr的限制:
constexpr的适用场景:
const:
constexpr:
示例 1:const
#include <iostream>
using namespace std;
int main() {
const int x = 5;
const int y = x + 3;
cout << y << endl;
return 0;
}
定义了一个 const 变量 x,并将其值设为 5。然后又定义了一个 const 变量 y,并将其设为 x + 3。这里的 x 和 y 都是在运行时确定的,因此编译器会在程序运行时对 y 的值进行计算。
示例 2:constexpr
#include <iostream>
using namespace std;
int main() {
constexpr int x = 5;
constexpr int y = x + 3;
cout << y << endl;
return 0;
}
constexpr 来定义 x 和 y。在这种情况下,编译器会在编译时就计算出 y 的值,因为 x 和 3 都是在编译时就可以确定的常量。因此,y 的值在编译时就被确定了。
小结:const和constexpr在编译时计算能力上的区别在于const仅指示变量的值在运行时不可修改,而constexpr则要求在编译时进行常量表达式的计算。constexpr更适用于那些需要在编译时确定值或进行编译时计算的场景,而const更适用于普通的常量声明。
const:
constexpr:
const 可以用于声明各种类型的常量,并且可以用于指针和引用的限制不是很严格;而 constexpr 主要用于声明常量,同时还可以用于指示编译时计算。constexpr 在赋值的范围上更加灵活,特别是在 C++14 中的扩展。
const:
constexpr:
当涉及到赋值的范围,const 和 constexpr 也有一些不同。让我们通过示例来说明这一点。
示例 1:const
#include <iostream>
using namespace std;
int main() {
const int x = 5;
const int* ptr = &x; // 指向常量的指针
cout << *ptr << endl;
*ptr = 10; // 编译器会报错,因为指针指向的是一个常量
return 0;
}
示例 2:constexpr
#include <iostream>
using namespace std;
int main() {
constexpr int x = 5;
constexpr int* ptr = &x;
cout << *ptr << endl;
*ptr = 10; // 编译器会报错,因为指针指向的是一个常量
return 0;
}
小结:const 主要用于指示对象或者成员函数不可修改,限定对象的只读访问行为,而 constexpr 用于指示在编译时可以进行求值的场景,特别适用于一些对值就能确定的操作。constexpr 对于类和对象的适用性更加注重在编译时进行的操作,而 const 更多是关于对象的只读性质。
const:
constexpr:
示例 1:const
#include <iostream>
using namespace std;
void printMessage(const string& msg) {
cout << msg << endl;
}
int main() {
const string message = "Hello, world!";
printMessage(message);
return 0;
}
示例 2:constexpr
#include <iostream>
using namespace std;
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int num = 5;
constexpr int result = square(num);
cout << result << endl;
return 0;
}
小结:const 主要用于标记函数参数和返回值的常量性质,以及在类中用于声明不会修改对象状态的成员函数;而 constexpr 则主要用于表示可以在编译时求值的函数,适用于对一些值在编译时就能确定的操作。constexpr 对于函数的适用性更加注重在编译时进行的操作,而 const 则更多用于标记常量性质。
const和constexpr的主要区别在于编译时进行的操作和值的确定性。
const:
constexpr:
示例 1:const
#include <iostream>
#include <vector>
using namespace std;
void printVector(const vector<int>& vec) {
for (const int& num : vec) {
cout << num << " ";
}
cout << endl;
}
int main() {
const int size = 1000000;
vector<int> numbers;
for (int i = 0; i < size; i++) {
numbers.push_back(i);
}
printVector(numbers);
return 0;
}
示例 2:constexpr
#include <iostream>
using namespace std;
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int num = 5;
constexpr int result = square(num);
cout << result << endl;
return 0;
}
从性能和效率的角度来看,constexpr 在编译时确定值的特性使得它更有可能比 const 变量更有效率。因为它减少了在运行时进行计算的需要,可以在适当的情况下提高程序的性能。但对于一般的常量定义,使用 const 或者 constexpr 都不会对程序的性能产生显著的影响。
// 使用 const 定义常量
#include <iostream>
using namespace std;
int main() {
const int max_value = 100;
cout << "The maximum value is: " << max_value << endl;
// max_value = 200; // 这行代码会导致编译错误,因为常量不可更改
return 0;
}
// 使用 constexpr 定义常量
#include <iostream>
using namespace std;
int main() {
constexpr int max_value = 100;
cout << "The maximum value is: " << max_value << endl;
return 0;
}
const:
constexpr:
常量有不同的用法和适用场景:
const变量:
constexpr变量:
#define预处理器宏:
枚举类型(enum):