C++ 常量

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 指针之间有什么区别?

指向 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 成员函数的目标相冲突,这就是为什么我们不能执行该函数调用。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战