您的位置:首页 > 编程语言 > C语言/C++

C++之const

2017-03-15 11:54 106 查看
一、使用const修饰指针和常量

首先来看一组定义:

int a = 23;

int* const p1 = &a;

int const *p2 = &a;

const int *p3 = &a;

const int* const p4 = &a;

根据《Effective C++》中条款03:如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针本身是常量;如果出现在星号两边,表示指针和被指物两者都是常量。

下面看代码示例;

#include<iostream>
using namespace std;

int main()
{
int a = 23;
int b = 32;
cout<< &a <<endl;

//指针p1不可以指向别的地方(指针为常量),但可以通过p1来修改a的值。
int* const p1 = &a;
cout<< p1 <<endl;
//p1 = &b; //错误
*p1 = b;    //可以
cout << a <<endl;   //a输出为32

int const *p2 = &a; //不可以通过p2来修改a的值,但p2可以指向别的地方
cout<< p2 <<endl;
//*p2 = b;  //错误,不可以通过p2来修改a的值
p2 = &b;    //可以

const int *p3 = &a; //与上面相同
cout<< p2 <<endl;
//*p2 = b;  //错误,不可以通过p2来修改a的值
p2 = &b;    //可以

const int* const p4 = &a;//即不可以指向别的地方也不可以通过p2来修改a的值
//p4 = &b;  //错误
//*p4 = b;  //错误

return 0;
}


二、用const修饰函数参数

如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const修饰,否则该参数将失去输出功能。const只能修饰输入参数。

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

如果输入参数使用“按值传递”,函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,因此不要加const修饰。

不要将函数void func1(int x); 写成void func1(const int x);

同理不要将函数void func2(A a) 写成void func2(const A a)。其中A为用户自定义的数据类型。

对于非内部数据类型的参数而言,像void func2(A a) 这样声明的函数注定效率比较底。因为要产生临时变量,则函数体要调用对象的构造、复制、析构过程都将消耗时间。

为了节约时间、提高效率,我们可以采用“引用传递”,因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。另外我们也可以像这样void func2(cont A& a);这样便可以防止意外修改参数a(当然是你不允许修改参数的情况)。

三、用const修饰函数的返回值

当函数之前加上const关键字时:

const A* fun3(const A& a);

如下语句出现编译器错误:

A* b = fun3(a);

但如果这样则不会出现;

const A* b = fun3(a);

以上情况说明:使用const修饰的函数返回值必须赋值给一个使用const修饰得类型,否则编译出错。

但是如下情况应注意:

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

int GetInt(void) 写成const int GetInt(void)。

2)同理不要把函数A GetA(void) 写成constA GetA(void),其中A为用户自定义的数据类型)

3)如果返回值不是内部数据类型,将函数A GetA(void) 改写为const A &GetA(void)的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。

classA

{

public:

A & operate = (const A &other); // 赋值函数

};

Aa, b, c; // a, b, c 为A的对象

a= b = c; // 正常的链式赋值

(a= b) = c; // 不正常的链式赋值,但合法

如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。上例中,语句a= b = c 仍然正确,但是语句(a= b) = c 则是非法的。

四、const 成员函数

将const实施于成员函数的目的,是为了确认该函数成员可作用于const对象身上。第一,使class接口比较容易被理解。因为这样可得知那个函数可以改动对象内容而那个函数不可以。第二,它使“操作const对象”成为可能,因为编写高效代码关键之一是以pass by reference-to-const方式传递的。

任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这样会增强函数的稳定性。以下程序中,类stack的成员函数Get Count()仅用于计数,从逻辑上讲GetCount应当为const函数。编译器将指出GetCount函数中的错误。

classStack

{

public:

void Push(int elem);

int Pop(void);

intGetCount(void) const; // const 成员函数

private:

intm_num;

int m_data[100];

};

int Stack::GetCount(void)const

{

++ m_num; // 编译错误,企图修改数据成员m_num

Pop();// 编译错误,企图调用非const函数

returnm_num;

}

Const函数的几点规则:

a.const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.

b.const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.

c.const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.

e.然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的

至于为什么const要放在函数的后面,原因之一应该是没有地方放了。const成员函数中的const其实是修饰this指针指向的对象,因为每个成员函数都会隐藏一个this指针。如类(A)成员函数:

int fun4(int n) const;

实际上为:

int fun4(A* const this, int n) const;

其中函数后面的const作用点在const A* const this,修饰this指针指向的对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  const