const
关键字(代表 **constant**,即常量)用于指定一个变量的值在程序中的任何地方都不能被更改,无论是故意的还是意外的。例如,
// makes PI a constant
const double PI = 3.14;
注意: 常量的标准命名约定是所有字母都必须大写。例如,
const int MAGIC_NUMBER = 42;
创建 const 变量
const
变量必须在声明时进行初始化,之后不能更改。
// invalid since PI is not initialized
const double PI;
在此,我们会收到一个错误,因为 const
变量 PI 没有被初始化。
相反,我们应该通过以下方式初始化 const
变量:
// initialize const with a value
const double PI = 3.14;
示例 1:C++ const
#include <iostream>
using namespace std;
int main() {
// initialize a const PI
const double PI = 3.14;
int radius = 4;
// use the const in a calculation
double area = PI * radius * radius;
cout << "Area of circle with radius " << radius << " is: " << area;
return 0;
}
输出
Area of circle with radius 4 is: 50.24
在上面的示例中,我们初始化了一个常量变量 PI。
const double PI = 3.14;
然后,我们使用这个常量变量来计算圆的面积。
double area = PI * radius * radius;
Const 引用
我们也可以使用 const
关键字创建引用。有不同类型的此类引用,具有不同的行为。它们是:
引用类型 | 描述 |
---|---|
对常量变量的常量引用 | 变量和引用都是常量的。 |
对非常量变量的常量引用 | 变量非常量,但引用是常量的。 |
对常量变量的 Const 引用
在 C++ 中,我们可以使用引用变量创建对 const
变量的 const
引用。例如:
#include <iostream>
using namespace std;
int main() {
// initialize a constant PI
const double PI = 3.14;
// create a read-only reference to PI
const double &PI_REF = PI;
cout << "PI: " << PI;
cout << "\nPI_REF: " << PI_REF;
return 0;
}
输出
PI: 3.14 PI_REF: 3.14
在上面的示例中,我们创建了一个常量引用,指向常量,或者说是一个常量引用。
因此,如果我们尝试修改 PI_REF 的值,我们会收到一个错误。
// error: assignment of read-only reference 'PI_REF'
PI_REF = 90;
常见问题
是的,常量引用可以引用非常量变量。但是,我们不能通过该引用修改该值。例如:
#include <iostream>
using namespace std;
int main() {
int num = 10;
// constant reference to a non constant variable
const int &NUM_REF = num;
cout << NUM_REF;
return 0;
}
输出
10
在上面的示例中,我们创建了一个指向非常量变量的常量引用。
因此,我们不能通过引用变量 NUM_REF 来修改 num 的值。
// error: assignment of read-only reference 'NUM_REF'
NUM_REF = 67;
作为常量引用传递
我们可以使用常量参数来确保传递的实参不会被函数体修改。
例如,我们可以通过以下代码来确保传递的 vector 不会被修改:
int sum(const vector<int> nums) {
// function body
}
虽然上面代码中的 const
关键字保证 nums 不会被修改,但它会创建一个 nums 的局部副本,这会消耗内存。
更好的处理方法是通过常量引用传递,使用以下代码:
int sum(const vector<int> &nums) {
// function body
}
这里,&nums 是一个引用,而 const
关键字表示它是一个常量。
示例 3:作为常量引用传递
#include <iostream>
#include <vector>
using namespace std;
// take a const reference as argument
int sum(const vector<int> &nums) {
int total = 0;
for(auto num: nums){
total += num;
}
return total;
}
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
cout << "Sum: " << sum(nums);
return 0;
}
输出
Sum: 15
在上面的示例中,我们将 nums vector 作为常量引用传递。
int sum(const vector<int> &nums) {
// code
}
这里,我们没有创建 nums vector 的局部副本,而是传递了一个不能被修改的引用。这更快,也更节省空间。
Const 和指针
与引用一样,我们也可以使用 const
关键字创建不同类型的指针。它们是:
指针类型 | 描述 |
---|---|
指向 Const 的指针 | 指针本身不是常量的,但它指向的值是常量的。 |
Const 指针 | 指针本身是常量的,但它指向的值不是常量的。 |
指向 Const 的 Const 指针 | 指针和它指向的值都是常量的。 |
指向 Const 的指针
指向 Const 的指针是 指针 变量,它指向一个常量变量。这些指针:
- 允许我们更改指针指向的地址,
- 不允许我们更改那些常量变量中存储的值。
例如,
#include <iostream>
using namespace std;
int main() {
// initialize a constant TOTAL_MONTHS
const int TOTAL_MONTHS = 12;
// MONTHS_PTR is a pointer to an int const
const int *MONTHS_PTR = &TOTAL_MONTHS;
cout << "TOTAL_MONTHS: " << TOTAL_MONTHS << endl;
cout << "*MONTHS_PTR: " << *MONTHS_PTR << endl;
// create another int constant
const int HALF_MONTHS = 6;
// MONTHS_PTR now points to HALF_MONTHS
MONTHS_PTR = &HALF_MONTHS;
cout << endl;
cout << "HALF_MONTHS: " << HALF_MONTHS << endl;
cout << "*MONTHS_PTR: " << *MONTHS_PTR;
return 0;
}
输出
TOTAL_MONTHS: 12 *MONTHS_PTR: 12 HALF_MONTHS: 6 *MONTHS_PTR: 6
在上面的示例中,我们创建了一个整数常量 TOTAL_MONTHS。然后,我们创建了一个指向该常量的指针,称为 MONTHS_PTR。
// points to const int TOTAL_MONTHS
const int *MONTHS_PTR = &TOTAL_MONTHS;
如果我们尝试修改 MONTHS_PTR 的值,我们会收到一个错误。
// error: assignment of read-only location '* TOTAL_MONTHS_PTR'
*TOTAL_MONTHS_PTR = 10;
这是因为 TOTAL_MONTHS 的值是常量的,不能被修改。
但是,我们可以更改 MONTHS_PTR 指向的地址,使其指向另一个变量,而不会出现任何错误。
const int HALF_MONTHS = 6;
MONTHS_PTR = &HALF_MONTHS;
Const 指针
Const 指针是指在初始化后,我们不能更改它所指向的变量,但可以更改指针指向的值。例如:
#include <iostream>
using namespace std;
int main() {
string country1 = "Nepal";
string country2 = "USA";
cout << "Initially, country1: " << country1 << endl;
// PTR1 is a const pointer to country1
string *const PTR1 = &country1;
// change the value of country1 using PTR1
*PTR1 = country2;
cout << "Finally, country1: " << country1;
return 0;
}
输出
Initially, country1: Nepal Finally, country1: USA
在上面的示例中,我们创建了一个const 指针。
// PTR1 is a const pointer to a string
string *const PTR1 = &country1;
最初,country1 的值为 Nepal
。然后,我们通过 PTR1 修改了 country1 的值。
// change the value of variable pointed by PTR1
*PTR1 = country2;
所以 country1 的新值变为 USA
。
但是,如果我们尝试将 PTR1 指向的变量从 country1 更改为 country2,我们会收到一个错误。
// error: assignment of read-only variable 'PTR1'
PTR1 = &country2;
指向 Const 的 Const 指针
指向 Const 的 Const 指针是一种指针,通过它可以既不能更改它指向的变量,也不能更改它的值。例如:
#include <iostream>
using namespace std;
int main() {
// create a const variable
const string COUNTRY1 = "Nepal";
// create a const pointer to const
const string *const PTR = &COUNTRY1;
cout << *PTR;
return 0;
}
输出
Nepal
常见问题
指向 Const 的指针是指一个指针,它指向一个不应通过该指针更改的值。
另一方面,Const 指针是一个指针,在初始化后不能指向不同的地址。
例如,
1. 指向 Const 的指针
const int* ptr_to_const = &value;
// Error: Can't modify the value pointed to
*ptr_to_const = 20;
// OK: The pointer can point to another address
ptr_to_const = &another_value;
2. Const 指针
int* const const_ptr = &value;
// OK: The value pointed to can be changed
*const_ptr = 30;
// Error: Can't change the address the pointer is pointing to
const_ptr = &another_value;
Const 表达式
常量表达式在 C++11 中引入。它们是那些:
- 值不能改变,
- 并且可以在编译时求值的表达式。
我们在 C++ 中使用它们的原因是为了避免在运行时多次执行计算。
我们使用 constexpr
关键字来创建常量表达式。例如:
// declare a function as constexpr
constexpr int add_numbers(int a, int b) {...}
// declare a variable as constexpr
constexpr int sum = add_numbers(660, 6);
注意:我们不能使用 constexpr
变量调用未声明为 constexpr
的函数。
示例 4:C++ Const 表达式
#include <iostream>
using namespace std;
// a constexpr function that
// returns nth fibonacci number
constexpr int fib(int n) {
// returns n if n <= 1
// else, recursively calls fib(n - 1) + fib(n - 2)
return n <= 1 ? n: fib(n - 1) + fib(n - 2);
}
int main() {
// two constexpr variables that store
// the return values of constexpr function
constexpr int fibonacci_five = fib(5);
constexpr int fibonacci_ten = fib(10);
cout << "fib(5) : "<< fibonacci_five << endl;
cout << "fib(10) : "<< fibonacci_ten;
return 0;
}
输出
fib(5) : 5 fib(10) : 55
在上面的示例中,我们创建了一个常量表达式(以函数的形式)来计算第 n 个斐波那契数。
constexpr int fib(int n) {
return n <= 1 ? n: fib(n - 1) + fib(n - 2);
}
请注意,我们在返回类型前添加了 constexpr
关键字,这告诉编译器该函数将在编译时求值。
然后,我们使用两个 constexpr
变量计算第 5 个和第 10 个斐波那契数。
constexpr int fibonacci_five = fib(5);
constexpr int fibonacci_ten = fib(10);
现在,请注意我们正在进行普通的函数调用。唯一不同的是计算是在编译时完成的。
// function not declared as constexpr
int fib(int n) {...}
// error: call to non-'constexpr' function 'int fib(int)'
constexpr int fibonacci_five = fib(5);
但是,我们可以使用非常量变量来调用 constexpr
函数。
constexpr int fib(int n) {...}
// valid code
int fibonacci_five = fib(5);
Const 成员函数
Const 成员函数确保对象的成员变量不会被更改。我们使用以下语法创建此类函数:
return_type function_name() const {
// function body
}
Const 成员函数的属性
- 我们不能在 const 成员函数中更改成员变量的值。
- 我们只能从常量对象调用 const 成员函数。
示例 5:Const 成员函数
#include <iostream>
using namespace std;
class Rectangle {
private:
int breadth, length;
public:
Rectangle(int length, int breadth){
this->breadth = breadth;
this->length = length;
}
// constant member function
int get_area() const {
return length * breadth;
}
// non-constant member function
int get_perimeter() {
return 2 * (length + breadth);
}
};
int main() {
// create a const Rectangle object
const Rectangle RECT = Rectangle(7, 6);
// call the const member function
cout << "Area: " << RECT.get_area() << endl;
return 0;
}
输出
Area: 42
在上面的示例中,我们创建了一个名为 Rectangle
的类,它具有以下成员函数:
get_area()
- 一个 const 成员函数get_perimeter()
- 一个普通成员函数
在 main()
中,我们创建了一个名为 RECT 的常量对象。
// create a const Rectangle object
const Rectangle RECT = Rectangle(7, 6);
然后,我们使用常量对象 RECT 调用了const 成员函数 get_area()
:
// code to call const member function
RECT.get_area();
调用非 Const 成员函数
但是,如果我们尝试调用非 const 成员函数 get_perimeter()
,我们会收到一个错误。
// error
RECT.get_perimeter();
这是因为常量对象不能调用非 const 成员函数。
但是,非常量对象可以调用 const 和非 const 成员函数。
这样做的原因很简单:我们创建一个常量对象是为了不修改数据成员。但是非 const 成员函数有能力修改数据成员。
因此,常量对象的目标与非 const 成员函数的目标相冲突,这就是为什么我们不能执行该函数调用。