- 记录const的使用特点,参考自C++ primer plus 附录
1、使用const 而不是 #define 来定义常量
- 在C/C++中定义常量通常使用
const
关键字,当然你也可以使有宏#define
来定义。 - 这两种方式定义常量如下所示:
// const 定义常量
const int PI = 3.14;
// 宏定义常量
#define PI = 3.14
- 首先介绍一下
#define
是如何工作的: - 编译器在编译我们写好C/C++程序时,其编译器过程为:预编译->编译->链接。
#define
的替换就是在预编译阶段完成的,也就是说在预编译阶段将C++中的所有用到的宏都用宏定义中的值替换掉。
- 说说使用
const
的优越性 - <1>.
const
显式的指明了类型,而使用#define
必须加后缀指出类型,如使用 100L 来表示long类型。 - <2>.
const
方法可以很方便的用于复合类型,如:
const int base_vals[3] = {10,20,30};
const string ans[3] = {"yes", "no", "maybe"};
- <3>.
const
标识符遵循变量的作用域,因此,可以创建作用域为全局、名称空间、数据块的常量,在特定情况下,不必担心定义会与程序的其他地方使用的全局变量冲突。
例如:
#define n 5
const int dz = 12;
...
void fizzle() {
int n;
int dz;
...
}
- 预处理器把
int n;
替换为int 5;
, 导致编译出错。 - 而
fizzle()
中定义的dz
是本地变量,必要时可以通过作用域解析运算符(::
),以::dz
的方式访问该常量。
所以,对于符号常量,习惯上还是使用
const
,而不是#define
。
2、const 与 变量的链接性
- 和
C语言
一样,C++
也为静态持续变量提供了三种链接性: - <1>. 外部链接性(可在其他文件中使用)
- <2>. 内部链接性(可在当前文件中访问)
- <3>. 无链接性 (可在当前代码块中访问)
- 在默认情况下全局变量的链接性为外部,但是
const
全局变量的链接性为内部,也就是说,在C++看来,全局const
定义就像使用了static
说明符一样:
const int fingers = 10;
// same as
static const int fingers = 10;
重点1:
内部链接性意味着每一个文件都有属于自己的一组常量,这也是可以将常量定义放在头文件的原因。
只要在两个源代码文件中包括同一个头文件,则他们可使用同一组常量。重点2:
使用 extern 可以覆盖变量默认的内部链接性,使其具有外部链接性,如:
extern const int states = 50;
并在所有使用该常量的文件中使用extern来声明它。重点3:``重点2
中 与定义常规外部变量不同之处:定义常规外部变量时,不必使用extern关键字,但在使用时必须使用extern
。
3、const 与 变量
- 常变量:
const
+ 类型说明符 + 变量名- 常引用:
const
+ 类型说明符 + &引用名- 常对象: 类名 +
const
+ 对象名- 常数组: 类型说明符 +
const
+ 数组名[大小]
- 常成员函数: 类名::fun(形参) +
const
- 常指针:
const
+ 类型说明符* + 指针名 ,类型说明符* +const
+ 指针名
3.1 int const 和 const int 的区别
- 首先提示的是在 常变量、常引用、常对象、 常数组中,
const
与 “类型说明符
”或“类名
”的位置可以互换。指针特殊考虑。
// type const name 与 const type name 含义相同
// 普通类型
const int a=5;
int const a=5; // 二者相同
// &
int const &bb = num;
const int& bb = num; // 二者相同
// 对于指针, 此处 *pt看为 name
int age = 10;
const int *pt = &age; // *pt 的值为 const,不能被修改
int const *pt = &age; // 二者相同
3.2 Const 与 指针
- 将
const
用于指针的方式,可以分为两种不同方法: - <1>.
让指针指向一个常量对象
,这样可以防止使用改指针修改所指向的值。可以将非指const针赋给const指针
// <1>. 让指针指向一个常量对象
int age = 10;
const int *pt = &age; // 对 pt而言,age 的值为 const,不能被修改
// 可以通过age来修改age的值,不可以用 pt指针修改它
// 该方法可以防止pt修改指向的值, 但可以将一个新地址赋给 pt
int age = 20;
pt = &age; // 此时,仍然不能使用pt修改指向的值
- <2>.
将指针本身声明为常量
,这样可以防止改变指指针向的位置。
// <2>. 将指针本身声明为常量
int sloth = 3;
const int * ps = &sloth;
int * const fig = &sloth; // 该声明使fig 只能指向sloth, 但允许使用fig 来修改sloth 的值。
4、const 与 函数
const
修饰函数参数可参考第三节。- 此处总结
const修饰函数体
、const修饰函数返回值
。
4.1 const修饰函数体
const
修饰类成员函数,称作const成员函数
。规则如下:- <1>.
const对象
只能访问const成员函数,非const对象
可以访问任意的成员函数(包括const成员函数)。 - <2>.
const成员函数
不可以修改对象的数据,(在编译时,以是否修改成员数据为依据,进行检查)。
此外,const对象
的成员是不可修改的,然而const对象
通过指针维护的对象却是可以修改的。
#include <iostream>
using namespace std;
class Point{
public :
Point(int _x):x(_x){}
void testConstFunction(int _x) const {
// 错误-1
// 在const成员函数中,不能修改任何类成员变量:x;
x=_x;
// 错误-2
// const成员函数不能调用 非const成员函数:modify_x(_x);
// 因为 非const成员函数 可能会修改成员变量:x
modify_x(_x);
}
void modify_x(int _x){
x=_x;
}
int x;
};
4.2 const修饰函数返回值
- 返回指向
const
对象的引用: - <1>. 返回对象将调用复制函数,返回引用则不会。
- <2>.
引用指向的对象
应该在调用函数期间存在(该对象不是函数中新建的临时变量)。 - <3>. 返回类型为
const引用
时,接收也应该使用const引用
。
Point Point_A(1);
Point Point_A(2);
// 返回 const对象的引用
const Point& max(const Point& one, const Point& two){
// ...
return one;
}
- 要点总结
- <1>. 如果函数返回的是局部对象(
临时变量
),则应该返回对象(复制构造函数
),而不是返回对象的引用。 - <2>. 如果函数返回的对象(该类无复制构造函数),返回一个指向对象的引用。
- <3>. 返回引用效率更高。