const限定符
const 允许定义一个语义约束(也就是指定一个”不该被改动“的对象),而编译器会强制实施这个约束;它允许你告诉编译器和其他程序员这个值应该保持不变;如果某个值要保持不变就应该使用const限定符,来获得编译器的襄助,确保这个约束。
const 的作用
1. 定义const常量
const double AspectRatio = 1.6667;
const 常量 与 宏 的比较:
#define ASPECTRATIO = 1.6667;
const常量肯定被编译器看到,当然会进入记号表内,编译器可以对const常量进行类型安全检查,而宏只是简单的字符串替换,没有类型的安全检查。
const 常量可能比使用#define 导致较小量的码,因为预处理可能盲目的将ASPECTRATIO替换成1.6667,从而导致目标吗出现多份。
2. 防止被修改,起到保护作用
void add(const int x,const int y)
{
x++;//error
}
3. 可以提供函数重载
class TexBlock{
public:
const char& operator[](std::size_t position) const;
char& operator[](std::size_t position);
}
4. 提高效率
编译器通常部位普通的const 常量分配储存空间,二十将他们保存在符号表中,这使得它成为一个编译器期间的常量,没有了存储与读内存的操作,使得它的效果很高。
const的使用
1. 定义常量
const double AspectRatio = 1.6667;
2. 指针使用const
char greeting[] = "hello C++";
char *p = greeting; //non-const pointer,non-const data
const char* p = greeting; //non-const pointer,const data
char * const p = greeting; //const pointer,non-const data
const char* const p = greeting; //const pointer,const data
如果关键字 const 出现在星号左边,表示被指物是常量;
如果const 出现在星号的右边,表示指针是const 常量;
如果const 出现在星号的两边,那么指针和指向物都是常量。
3. const 在STL 迭代器中的使用
STL迭代器以指针作为根据塑模出来的,所以迭代器二代额作用就像T* 指针;声明迭代器为const 就像声明指针为const一样(即声明一个T*const 指针),表示这个迭代器不得指向别的东西,但是它所指向的东西是可以改变的;如果希望迭代器所指向的东西不可被改动(即希望STL 模拟一个 const T* 指针),需要使用 const_iterator。
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); //iter 的作用像个T* const
*iter = 10; //ok,改变iter所指物
++iter; //error iter是const
std::vector<int>::const_iterator cIter = vec.begin();
*cIter = 10; //error,*cIter是const
++cIter; //ok,改变cIter
4. const 对象默认为文件的局部变量
在全局作用域中定义非const 变量时,他在整个程序中都可以访问;可以把一个非const 变量定义在一个文件中,假设已经做了合适的声明,就可在另外的文件中使用这个变量。
//file_1.cc
int counter; //definition
//file_2.cc
extern int counter;//use counter from file_1
++counter;
在全局作用域声明的const 变量是定义该对象的文件的局部变量;此变量只存在于那个文件中,不能被其他文件访问;通过指定const 变量为 extern ,就可以在整个程序中访问 const 对象。
//file_1.cc
extern const int bufSize = fcn();
//file_2.cc
extern const int bufSize;//这里的bufSize只是一个声明
非const 变量默认为 extern ,要使const 变量能够在其他文件中访问,必须显式的指定它为 extern。
5. const 引用
const 引用是指向 const 对象的引用。
const int ival = 1024;
const int &refVal = ival;//ok: both reference and object are const
int &ref2 = ival;//error: nonconst reference to a const object
const 可以 初始化为不同类型的对象或初始化为右值,如字面值常量:
int i= 42;
const int &r1 = 42;//ok
const int &r2 = r + i;//ok
int &refVal3 = 10; // error : initializer must be an object
//将引用绑定到不同类型
double dval = 3.14;
const int &ri = dval;//ok
//上述等同于
int temp = dval;
const int &ri = temp;
非 const 引用只能绑定到与该引用同类型的对象,const 引用则可以绑定到不同但是相关的类型的对象或者绑定到右值。
6. const 在函数中的使用
1. 函数返回使用const
class Rational { ... };
const Rational operator* (const Rational& lhs, const Rational& rhs);//返回右值
//避免如下错误
Rational a,b,c;
if( a * b = c){//应该是个比较动作
}
2. 函数参数使用
(1)传递过来的参数及指针本身在函数内不可变,无意义!
void func(const int var); // 传递过来的参数不可变
void func(int *const var); // 指针本身不可变
表明参数在函数体内不能被修改,但此处没有任何意义,var本身就是形参,在函数内不会改变。包括传入的形参是指针也是一样。输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。
(2)参数指针所指内容为常量不可变
void StringCopy(char *dst, const char *src);
其中src 是输入参数,dst 是输出参数。给src加上const修饰后,如果函数体内的语句试图改动src的内容,编译器将指出错误。这就是加了const的作用之一。
(3)参数为引用,为了增加效率同时防止修改。
void func(const A &a)
对于非内部数据类型的参数而言,象void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数void func(A &a) 存在一个缺点:“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为
void func(const A &a)。
以此类推,是否应将void func(int x) 改写为void func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
小结:对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void func(A a) 改为void func(const A &a)。对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void func(int x) 不应该改为void func(const int &x)。
以上解决了两个面试问题:
(1)如果函数需要传入一个指针,是否需要为该指针加上const,把const加在指针不同的位置有什么区别;
(2)如果写的函数需要传入的参数是一个复杂类型的实例,传入值参数或者引用参数有什么区别,什么时候需要为传入的引用参数加上const。
7. const 在类中的使用
1. const 成员函数
将 const 实施于成员函数的目的,是为了确认该成员函数可作用于const对象身上,这一类成员函数之所以重要,基于一下两个原因:
1. 他们使class 接口比较容易被理解,哪个函数可以改动对象内容哪个不能,这很重要。
2. 他们使操作”const“对象 成为可能
2. const 成员变量
对于类中的const成员变量必须通过初始化列表进行初始化,如下所示:
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
3. const 对象
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.
class TextBlock{
public:
const char& operator[](std::size_t position) const;
char& operator[](std::size_t position);
}
TextBlock tb("hello");
std::cout << tb[0];//调用non-const TextBlock::operator[]
const TextBlock ctb("world");
std::cout << ctb[0];//调用const TextBlock::operator[]
4. 静态成员变量
#include <iostream>
using namespace std;
class A
{
public:
A(){}
int val();
static int stval();
static int ac;
const static int bc=2;//常量静态成员可以在类内初始化
private:
static int dc;
int nn;
};
以上参考
- Effective C++
- C++ Primer
- https://github.com/Light-City/CPlusPlusThings