C++ 类中的const用法总结:
先看一个例子:
class A
{
public:
A(int x) : num(x), b(x) {}
void fun(const A& a); //const修饰函数形参
int GetNum(void) const;//const修饰不修改成员变量的函数
void SetNum(int x);
A& operator=(const A& other); //const修改成员函数的返回值和形式参数
const A operator*(const A& left, const A& right); //const 修改成员函数的返回值
private:
int num; //普通成员
const int b; //const成员
static int c; //静态成员
static const int d; //静态常量成员
};
int A :: c = 0;
const int A :: d = 0;
int A :: GetNum(void) const
{
return num;
}
void A :: SetNum(int x)
{
num +=1;
}
const A A :: operator*(const A& left, const A& right)
{
return A(left.num*right.num);
}
一、 const成员变量
1. const 成员的初始化: 不能在定义处初始化,只能在构造函数列表中初始化,而且必须要有构造函数
原因:const数据成员只在某个对象生存期内饰常量,而对于整个类而言确实可变的。
因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const数据成员的值是什么。
2. static 的const 成员的初始化 遵循static的初始化规则,在类定义的外边初始化
要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。
二、const成员函数
1. const修饰类的成员函数的形参
void fun(const A& a); //(1)
void fun1(A a); //(2)
第二个函数效率低。 函数体内产生A类型的临时对象用于“值传递”参数a,临时对象的构造、复制、析构过程都将消耗时间
而第一个函数提高了效率,用“引用传递”不需要产生临时对象,省了临时对象的构造、复制、析构过程消耗的时间;
但只要引用有可能改变a,所以加const。
总结:
(1) 参数const通常用于参数为指针或引用的情况,且只能修饰输入参数,保护其内容不被修改;
若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用cosnt。
(2) 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是为了提高效率;
(3) 对于内部数据类型的输入参数,不要将"值传递“的方式改为”const 引用传递“。否则即达不到提高效率的目的,又降低了 函数的可理解性、如
void Func(int x) 不应该改为:void Func(const int& x)
(4) static 成员函数不能声明为const的
原因:const的成员函数为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*;
但static成员函数是没有this指针的。
2. const修饰类的成员函数
int GetNum(void) const; //(1)
void SetNum(int x); //(2)
一些成员函数改变对象,如(2),一些成员函数不改变对象,也即不修改对象的数据成员,如(1)。
为了使成员函数的意义更加清楚,我们可在不改变对象的成员函数的函数原型中加上const 说明。
(1)任何不需要修改数据成员的函数都应该使用const修饰
这样即使不小心修改了数据成员或者调用了非const成员函数,编译器也会报错;
(2)const成员函数应该在函数原型声明和定义中都增加const限定,否则编译器会把它们看成是不同的函数
(3)const对象只能调用 由const显示修饰的const成员函数,不能调用该类的非const成员函数, 因为它可能企图修改常量的数据成员
例如:
const A a(1);
A.GetNum(); //right
A.SetNum(); //error
但构造函数和析构函数对这个规则例外,他们从不定义为常量成员,但是可被常量对象调用。
(4)const成员函数的作用
提高程序的可读性;(const成员函数不修改数据成员的值;非const成员函数修改)
提高程序的可靠性;(如果在const成员函数中修改数据成员的值,编译器按错误处理)
(5)定义const成员函数时,把const关键字放在函数的参数表和函数体之间。
为什么不放在函数声明前呢?->这样意味着函数的返回值是常量,意义完全不同。
3. const 修改成员函数的返回值
const A operator*(const A& left, const A& right);
这样声明了返回值,起到了保护作用。防止如下情况发生:
A a(1), b(2), c(3);
(a * b) = c;
总结:
(1) 一般用const 修饰返回值为对象本身(非引用或指针)的情况多用于二目操作符重载函数并产生新对象的时候;
但是通常不建议:用const修饰函数的返回值类型为某个对象或者某个对象引用的情况。
原因:如果返回值为某个对象,为const,(const A test = A 实例)或某个对象的引用为const(const A& test = A实例)
则返回值是const属性, 则返回实例只能访问A中的共有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到
(2) 如果给采用 ”指针传递“方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,则该返回值只能被赋给加const修饰的同类型指针。
如: const char * GetString(void);
char *str = GetString(); //error
const char* str = GetString(); //right
(3) 函数返回值采用”引用传递”的场合不多,一般只出现在类的赋值函数侯中,目的是为了实现链式表达:
A& operator=(const A& other);
A a,b ,c;
a = b =c; //right
(a = b) = c; //error
(4) const A& operator=(const A& a);这样可以么
(5) 不要轻易的将函数的返回值类型定位const
(6) 除了重载操作符外一般不要将返回值类型定为某个对象的const引用
Reference:
1. http://www.cnblogs.com/BloodAndBone/archive/2011/04/12/2013280.html
2. http://www.cnblogs.com/goodloop/archive/2010/04/13/1711368.html