C++ const的用法(非常详细)
程序的存在就是为了处理数据,程序中的数据非常重要,谁也不想让自己的数据未经授权就被随意修改而导致最终结果出错。因此,我们在 C++ 程序中通过设置类成员的访问级别来防止外界非法访问类中的数据,以此保护数据的安全性。
但 C++ 认为仅仅在类中设置访问级别对数据的保护还不够,所以专门增加了 const 关键字来保护数据,防止数据被非法修改。
我们知道,使用各种类型的变量来保存数据,如果想保护某个变量的值,使之保存的数据不被修改,可以在定义该变量时,在数据类型前加上 const 关键字进行声明。这样,一旦有人试图修改这个变量的值,编译器就会报错,从而使我们发现并阻止这种非法访问,确保数据的安全性。
使用 const 修饰变量的语法格式如下:
为了明确这两种形式的区别,我们可以把声明语句以“*”为界,分割成两个部分,如下图所示。

图 1 使用“*”分割常量整型指针和整型常量指针的声明语句
经过这样的分割后,很容易看出其中的区别:
因此,使用 const 声明或修饰指针变量,可以选择只保护指针指向的数据,或者只保护指针本身的值。我们甚至可以在“*”左右都加上 const,这样既保护它所指向的数据,也保护指针本身:
符号表是编译器在编译程序的过程中收集、记录和使用的程序代码中的语法符号的类型和特征等相关信息。这些信息一般以表格的形式存储于系统中,如常数表、变量名表、函数名表等。
如果使用宏,那么在调试时可能会遇到一个反复出现的数值,但不知道它的含义,这可能会给程序的调试带来一定的麻烦。相比之下,使用 const 定义常变量,不仅可以保证 PI 值不会被修改,变量名还会出现在符号表中,便于调试,同时可以进行类型检查,借助编译器减少错误。因此,在需要定义常量时,应优先选择使用 const。
相应地,对于负责传出数据的参数,因为在函数内部会被修改,自然就不应加上 const。因此,有无 const,可以清晰地表示函数参数是传入数据还是传出数据。
例如:
如果某个成员函数在语义上不会修改类的内部数据,例如 Rect 类的 GetArea() 函数,它只是用来得到矩形的面积,而不应该去修改矩形的任何数据。为了让这样的函数只做它该做的事,防止它修改类内部的数据,在声明函数时,可以在函数末尾加上 const 关键字。
负责监督的 const 关键字会检查该成员函数是否意外地修改了类的成员变量(做了不该做的事),一旦发现这类操作,编译器会给出错误提示信息,从而保护成员变量。例如:
除可以发现用 const 声明的成员函数中对数据的非法修改外,当在一个 const 声明的常量对象上调用非 const 声明的成员函数尝试对常量对象进行修改时,编译器也会发现这种非法修改,并报出编译错误来提示我们进行修正。例如:
但 C++ 认为仅仅在类中设置访问级别对数据的保护还不够,所以专门增加了 const 关键字来保护数据,防止数据被非法修改。
我们知道,使用各种类型的变量来保存数据,如果想保护某个变量的值,使之保存的数据不被修改,可以在定义该变量时,在数据类型前加上 const 关键字进行声明。这样,一旦有人试图修改这个变量的值,编译器就会报错,从而使我们发现并阻止这种非法访问,确保数据的安全性。
使用 const 修饰变量的语法格式如下:
const 数据类型 变量名;例如,可以这样来保护数据:
const double PI = 3.14159; PI = 3.14; // 有const保护,行不通用 const 声明(或称为修饰)后,变量就具有了 const 属性。当在程序中试图修改这个变量的值时,编译器就会报错。
C++ const声明指针变量的两种形式
如果我们要使用 const 声明普通数据类型的变量,只需在定义变量时在数据类型前加上 const 关键字即可。但如果我们要使用 const 声明一个指针类型的变量,则有两种形式:int N = 0; const int* pInt1 = &N; // 第一种形式:常量整型指针 int* const pInt2 = &N; // 第二种形式:整型常量指针由于 const 关键字位置的不同,这两种形式所表达的含义也各不相同。第一种形式定义的是一个常量整型指针,而第二种形式定义的是一个整型常量指针。这听起来像在说绕口令,实际上这两种形式确实有所不同。
为了明确这两种形式的区别,我们可以把声明语句以“*”为界,分割成两个部分,如下图所示。

图 1 使用“*”分割常量整型指针和整型常量指针的声明语句
经过这样的分割后,很容易看出其中的区别:
- 如果 const 在“*”的左边,则表示 const 修饰的是 int 类型,也就是说不能通过这个指针修改它所指向的 int 类型变量的值,但指针本身的值是可变的,它可以指向另一个 int 类型的变量;
- 如果 const 在“*”的右边,则表示 const 修饰的是指针本身,这意味着这个指针的值不能在定义后被修改,所以在定义的同时必须赋初值,同时也不能再指向其他的 int 类型变量,但可以通过这个指针修改它所指向的 int 变量的值。
因此,使用 const 声明或修饰指针变量,可以选择只保护指针指向的数据,或者只保护指针本身的值。我们甚至可以在“*”左右都加上 const,这样既保护它所指向的数据,也保护指针本身:
const int* const pInt3 = &N; // 双保险有了 const 的看管,再加上编译器的帮助,我们自然不用担心数据被非法修改了。
C++ const代替#define定义常变量
例如,定义表示圆周率的 PI 常量,可以采用下面的两种方式:// 定义宏PI #define PI 3.1415926 // 定义常量PI const double PI = 3.14159这两种方式在语法上都是合法的,但第二种方式比第一种方式好。原因在于,如果使用 #define 定义宏 PI,PI 会在预处理过程中被替换成具体的数字 3.14159,而宏的名称不会出现在程序的符号表中。
符号表是编译器在编译程序的过程中收集、记录和使用的程序代码中的语法符号的类型和特征等相关信息。这些信息一般以表格的形式存储于系统中,如常数表、变量名表、函数名表等。
如果使用宏,那么在调试时可能会遇到一个反复出现的数值,但不知道它的含义,这可能会给程序的调试带来一定的麻烦。相比之下,使用 const 定义常变量,不仅可以保证 PI 值不会被修改,变量名还会出现在符号表中,便于调试,同时可以进行类型检查,借助编译器减少错误。因此,在需要定义常量时,应优先选择使用 const。
C++ const修饰函数参数
因为用 const 声明的变量具有不可修改性,所以常用于给函数的传入参数加上 const 关键字,以表示这是一个只用于传入数据的参数,防止它在函数内部被非法修改而引起其他错误。相应地,对于负责传出数据的参数,因为在函数内部会被修改,自然就不应加上 const。因此,有无 const,可以清晰地表示函数参数是传入数据还是传出数据。
例如:
// 复制字符串函数 char* strcpy( char* destination, // 目标字符串,会被修改,所以没有加上 const 进行声明 const char* source // 源字符串,不会被修改,所以加上 const 进行声明 );
C++ const声明类成员函数
在类的定义中,除通过设置不同的访问级别来保护类内部的成员数据不会被修改外,const 关键字也能提供额外的保护。如果某个成员函数在语义上不会修改类的内部数据,例如 Rect 类的 GetArea() 函数,它只是用来得到矩形的面积,而不应该去修改矩形的任何数据。为了让这样的函数只做它该做的事,防止它修改类内部的数据,在声明函数时,可以在函数末尾加上 const 关键字。
负责监督的 const 关键字会检查该成员函数是否意外地修改了类的成员变量(做了不该做的事),一旦发现这类操作,编译器会给出错误提示信息,从而保护成员变量。例如:
// 矩形类 class Rect { public: // ... // 获得矩形面积,不应该修改类的数据,所以在函数末尾加上 const int GetArea() const { // 试图在用 const 声明的成员函数内修改类的成员变量 // 会导致一个编译错误 m_nW = 10; return m_nW * m_nH; // 只读访问成员变量 } // 设置矩形的长和宽 // 函数参数 nW 和 nH 只是用于传入数据,所以加上 const // 但在函数内部,会修改类的成员数据,所以没有加 const void SetRect(const int nW, const int nH) { m_nW = nW; // 修改成员变量 m_nH = nH; } private: // 设置私有访问级别保护数据 int m_nW = 0; int m_nH = 0; };在这段代码中,GetArea() 成员函数使用 const 进行声明,表示这是对类的一个只读访问。如果试图在 GetArea() 函数内部修改该类的数据,编译器会报出错误,这有助于我们检查程序代码中对数据的非法修改,并纠正程序的错误。
除可以发现用 const 声明的成员函数中对数据的非法修改外,当在一个 const 声明的常量对象上调用非 const 声明的成员函数尝试对常量对象进行修改时,编译器也会发现这种非法修改,并报出编译错误来提示我们进行修正。例如:
// 定义一个用 const 声明的常量对象 const Rect rect; // 在常量对象上调用非 const 声明的成员函数是非法的 rect.SetRect(3,4); // 在常量对象上调用以 const 声明的成员函数是合法的 int n = rect.GetArea();const 不仅可以看管单个变量,还可以看管函数参数,把 const 关键字放到类成员函数的末尾时,还可以保护整个类的数据。一旦发现对它所保护的数据的非法访问,编译器就会给出错误提示信息。因此,有了 const 的保护,程序中的数据就不会被非法访问了。