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出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针本身是常量;如果出现在星号两边,表示指针和被指物两者都是常量。
下面看代码示例;
二、用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指针指向的对象。
首先来看一组定义:
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指针指向的对象。
相关文章推荐
- c++中为什么会引入const
- C++基本功:全面掌握const、volatile和mutable关键字
- [收藏]C++ Tips(1)--const
- c++中const的作用- -
- c++专题: const
- C++中Const关键字总结
- [收藏]C++ Tips(1)--const
- c++中const,static 的成员的初始化
- C++ 基础内容1,c++ 中的const
- C++中的mutable和const
- C++常类型(const)
- C++(二)const
- [收藏]C++ Tips(9)--函数参数中的const
- C++常类型(const)
- 更深入一点理解switch语句及c/c++对const的处理
- c++提高篇之const的深入提高[转贴]
- C++中static 和const的解释
- [收藏]C++ Tips(9)--函数参数中的const
- c++中const关键字使用详解
- C++关键字const简介