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;
};

 

 

以上参考