const的用法

修饰变量

修饰常量

与变量不同,有const修饰的量是常量,常量的值不可改变,在定义的时候必须初始化,使用未初始化的常量是错误的。

int a=3;  
const int b=a; 

修饰引用

指向常量的引用

const引用是指–指向const的引用,其实这样说也不恰当,和const指针一样,const引用可以与常量绑定,也可以与变量绑定,只是不能通过这个const引用来改变绑定对象的值,和const指针类似。

const int &ra=a;  
a=5;//正确  
ra=4;//错误,不能通过引用改变a的值 

非const引用不能与const对象绑定,因为常量a的值不可改变,但却可以通过非const引用来改变常量a的值,这样做是错误的,如下:

const int a=10;  
int &ra=a;//错误,非const引用不能绑定到const对象 

**由于引用本身不是对象,所以其初始化只是将它绑定到初始值对象上,**而且引用一旦初始化完成,引用将始终和它的初始值对象绑定在一起。引用的类型必须与所引用的对象类型一致,但是有例外情况:在初始化常量引用时可以用任意的表达式作为初始值,只要表达式的结果能转换成引用的类型。

1
2
3
4
5
6
7
const int ca = 1;      
int &rca = ca;//类型不匹配,将一个普通引用绑定到一个常量  
int i=42;  
const int &r1=i;//正确  
const int &r2=42;//正确,r2是const引用  
const int &r3=r1*2;//正确,r3是const引用  
int &r4=32;        //错误,32是字面值

若改成

1
2
int r=32;
int &r4=i;

就可以正常运行

引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。

例如:

double dval = 23;
const int &ri = dval; 

编译器将其转换为:

int tmp = dval; // double -> int
const int &ri = tmp; 

指向指针的引用

引用的对象可以为指针

int *pa = &a;
int *&rd = pa;

但是引用的对象不能为地址,如果一定要让引用绑定地址,需要让引用指向的对象和引用本身都为const

int a = 1;  
int *&ra = &a; //错误,因为ra为非常量引用,而a的地址值不是对象,编译器会产生临时对象存储a的地址值  
const int * const &rc = &a; //正确,rc为绑定到一个指向int常量的指针的常量引用  

修饰指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include<iostream>
using namespace std;
int main(){
int a1=3;   ///non-const data
const int a2=a1;    ///const data

int * a3 = &a1;   ///non-const data,non-const pointer
const int * a4 = &a1;   ///const data,non-const pointer
int * const a5 = &a1;   ///non-const data,const pointer
int const * const a6 = &a1;   ///const data,const pointer
const int * const a7 = &a1;   ///const data,const pointer

return 0;
}

const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效

修饰函数的参数

  1. 如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针。

     void StringCopy(char *strDestination, const char *strSource);
    
  2. 如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。

     不要将函数void Func1(int x) 写成void Func1(const int x)。
    
  3. 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。

     例如:将void Func(A a) 改为void Func(const A &a)。
    

    因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间;“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。

  4. 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。

    例如:void Func(int x) 不应该改为void Func(const int &x)。

    因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。

修饰函数的返回值

  1. 如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。

    例如:函数const char * GetString(void); 正确的用法是: const char *str = GetString(); //写为char *str = GetString();将出现编译错误

  2. 如果函数返回值采用“值传递”方式,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。

     例如:不要把函数int GetInt(void) 写成const int GetInt(void)。
    
  3. 函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数

修饰函数的定义体

任何不会修改数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,这无疑会提高程序的健壮性。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Stack  
{  
public:  
void Push(int elem);  
int Pop(void);  
int GetCount(void) const; // const 成员函数  
private:  
int m_num;  
int m_data[100];  
};  
int Stack::GetCount(void) const  
{  
++ m_num; // 编译错误,企图修改数据成员m_num  
Pop(); // 编译错误,企图调用非const 函数  
return m_num;  
}  

const关键字所起作用的本质,就是把隐藏着的默认的this指针参数,改成const类型。也就是说:假如void foo( )函数被编译器改写为 void foo(T pThis),则void foo( ) const将会被改写为void foo(const T pThis) 。

在函数末尾添加一个const,就相当于在隐藏的this参数类型前加一个const

const成员函数

const成员函数的调用

尽管函数名和参数列表都相同,char get( ) const成员函数是可以与char get( )并存的,可以形成重载!

我们假设调用语句为cs.get(),如果cs为non-const对象,则调用foo()。如果cs为const对象,则调用foo()const。

1
2
3
4
5
6
7
8
9
class Screen {  
public:  
char get(int x,int y);  
char get(int x,int y) const;  
}; 
const Screen cs;  
Screen cc2;  
char ch = cs.get(0, 0);  // 调用const成员函数  
ch = cs2.get(0, 0);     // 调用非const成员函数

假如没有提供get()const,则const cs调用get()将会报错。但假如是没有提供foo(),则non-const obj调用foo()const是完全没有问题的。也就是说,non-const对象可以调用const函数,但const对象不能调用non-const函数.

原因在于底层const和顶层const。当对象为const,调用函数时会传入一个const this指针,而函数的形参为非const指针,所以无法正确传入。而当对象为非const,调用函数时传入的非const指针可以被转换为const指针,所以能够正确传入。

小结

类中二函数都存在的情况下:

  1. const对象默认调用const成员函数,非const对象默认调用非const成员函数;

  2. 若非const对象想调用const成员函数,则需显式转化,如(const Student&)obj.getAge();

  3. 若const对象想调用非const成员函数,同理const_cast(constObj).getAge();(注意:constObj要加括号)

类中只有一函数存在的情况下:

  1. 非const对象可以调用const成员函数或非const成员函数;

  2. const对象只能调用const成员函数,直接调用非const函数时编译器会报错;

在const成员函数里面调用非const成员函数

直接在const成员函数里面调用非const成员函数会报错。

  1. 向const成员函数内传入一个非const对象指针或引用,然后通过该指针或引用来调用非const成员 数。

  2. 在const成员函数内部进行this指针的const_cast转换,将其变为非const指针,然后就可以直接调用非const成员函数。

  3. 使用mutable关键字直接忽略掉const函数的限制。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Test
{
public:
Test() : num(0)
{}
~Test(){}
int Get(Test& t) const
{
t.foo(5);
return num;
}
int Get(Test* p) const
{
p->foo(10);
return num;
}
void foo(int n)
{
num = n;
}
private:
int num;
};
void main()
{
Test t;
cout<<t.Get(t)<<t.Get(&t)<<endl;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class foo{
public:
void test1(){
cout << "I am not a const member function" << endl;
}
void test2()const{
foo *temp = const_cast<foo*>(this);//注意这个转换!!!
temp->test1();
}
};
int main(){
foo f;
f.test2();
return 0;
}

在非const成员函数中调用const成员函数

当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。若要在非const成员函数中调用const成员函数,可以用const_cast将this指针指向的对象转换为const类型,然后进行相应的函数调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class TextBlock{
public:
  //...
  const char& operator[](size_t position) const
  {
       //...
       return text[position];
  }
  char& operator[](size_t positon)
  {
     return const_cast<char&>(//该函数返回const char的引用,所以需要再进行一次const_cast
                const_cast<const TextBlock&>(*this)[position]//注意是先将this解引用,然后将this指向的对象强制转换为const类型
             );
  }
private:
   char text[max_text_len];
};

const的作用

const意味着“只读”

  1. 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。

  2. 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

  3. 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

转载:
http://blog.csdn.net/lihao21/article/details/8634876
http://blog.csdn.net/mysunshinetbg/article/details/48346195