C++类中const的用法

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

posted @ 2014-12-09 16:43  MelodyLIU  阅读(1517)  评论(0编辑  收藏  举报