const

const 是constant 的缩写,“恒定不变”的意思。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。所以很多C++程序设计书籍建议:“Use const whenever you need”。

  1. const常量
  2. 修饰函数参数、返回值,甚至函数的定义体

 

1、用const 修饰函数的参数

(1)用函数参数进行返回值的:不用const修饰,否则“指针传递/引用传递”都不能正确返回参数值;

(2)函数参数作输入:

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

例如StringCopy 函数:

void StringCopy(char *strDestination, const char *strSource);

其中strSource 是输入参数,strDestination 是输出参数。给strSource 加上const修饰后,如果函数体内的语句试图改strSource 的内容,编译器将指出错误。

  • 输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。

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

不要将函数void Func2(A a) 写成void Func2(const A a)

 

关于内部和非内部数据类型参数传递:

  • 对于非内部数据类型的参数而言,void Func2(A a)效率低,因为调用时,需要产生对实参赋值,产生一个临时对象,临时对象构造、拷贝、析构耗时,可改成void Func(A &a);而引用可能会修改a,故改成void Func(const A &a)。
  • 对于内置数据类型,没有必要写成void Func(const int &x),因为内部数据类型的参数不存在构造、析构的过程,复制也非常快,“值传递”和“引用传递”的效率几乎相当。

“const &”修饰输入参数的用法:

  • 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。
  • 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。

 

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

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

例如函数:const char * GetString(void);

如下语句将出现编译错误:char *str = GetString();

正确的用法是:const char *str = GetString();

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

例如不要把函数int GetInt(void) 写成const int GetInt(void)

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

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

 

函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。

示例:

class A  
{  
   A & operate = (const A &other); // 赋值函数
}; 
A a, b, c;
// a, b, c 为A 的对象 a = b = c; // 正常的链式赋值 (a = b) = c; // 不正常的链式赋值,但合法

解答:

  1. 如果将赋值函数的返回值加const 修饰,那么该返回值的内容不允许被改动。
  2. 上例中,语句 a = b = c 仍然正确,但是语句 (a = b) = c 则是非法的,因为a=b调用重载函数运算,a=b是成了const对象。

 

3、const 成员函数(const的作用:说明其不会修改数据成员)

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

示例:

以下程序中,类stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。

class Stack  
{  
public:  
    void Push(int elem);  
    int Pop(void);  
    int GetCount(voidconst// const 成员函数
private:  
    int m_num;  
    int m_data[100];  
};  
int Stack::GetCount(voidconst
{  
     ++m_num; // 编译错误,企图修改数据成员m_num
     Pop(); // 编译错误,企图调用非const 函数
     return m_num;  
}  

const 成员函数的声明看起来怪怪的:const 关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。

关于const函数的几点规则:

  1. const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。
  2. (因为const对象不能被修改,与const成员函数一致;而非const对象可以修改/不修改)
  3. const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的。
  4. const成员函数不可以修改对象的数据,不管对象是否具有const性质,它在编译时,以是否修改成员数据为依据进行检查。
  5. 加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的。

 

const function使用实例:

(1)const 函数只能调用 const 函数,即使某个函数本质上没有修改任何数据,但没有声明为const,也是不能被const函数调用的。

示例1

class my {  
public:  
    string& operator[](const string& s) const
    { return table[s]; }  //编译错误
private:  
    map table;  
};  

解答:

当本重载函数声明为const后,就只能调用其他的同样也声明为const的函数;而table[s]实际上调用的是map的下标重载运算函数,该函数并没有声明为const。所以去掉本函数的const声明,就可以顺利通过编译了。

 

示例2

class A{  
public:  
    A(int n) { num = n; }  
    void incr() { num += 5; }  
void disp() const { cout << numspan> int times(int m) { return num * m; } int f() const {   incr();//passing `const A' as `this' argument of `void A::incr()' discards qualifiers   disp();// ok   times(2);//passing `const A' as `this' argument of `int A::times(int)' discards qualifiers   return num; }
private: int num; }; int main(int argc, char *argv[]) { A a(5); a.f(); system("PAUSE"); return EXIT_SUCCESS; }

示例3:

DateTime类是自定义的,isvalidDateTime类的方法。

bool DateTime::earlier(const DateTime &dt){
    if(!isvalid()) return true;
    if(!dt.isvalid()) return false;
    //出错,提示 The non-const member function "DateTime::isvalid()" is called for "const DateTime"

解答:

  1. 每个非staticconst成员函数都有一个隐含的this指针,是非const型的,不能接受const型实参;
  2. 在你的函数里,dtconst DateTime &型的,dt.isvalid()相当于将const对象传给非constthis指针,所以出错。
  3. 如果isvalid不改变对象状态,可以将earlier成员函数定义为const型。

 

如果用const来修饰函数,那么函数一定是类的成员函数。

const 类型的成员函数不能返回非const类型的引用。

示例:

class Test  
{  
public :  
    int & GetValue()const;  
    private:  
    int value;  
};  

int &Test::GetValue() const
{  
    return value; //value此时具有const属性,与返回值类型int &的非const属性不匹配
}  

解答:

  1. 这样的代码在vs2003中提示的错误:error C2440: return: 无法从“const int”转换为“int &”。
  2. const函数中传递this的时候把this变成了const T* const this,所以一个非const的引用指向一个const类型的变量,就会error

修改:

  1. int value 改成mutable int valuemutable修饰的变量使之在const函数中可以被改变的。
  2. return value 改成。 return const_cast(value)const_cast去掉了const性质。
  3. 把函数写成const int &Test::GetValue() const ,.这样做的目的是使引用的变量也是const类型的,就相当于const int & b
  4. 把引用去掉,写成返回值类型的。
  5. 把函数后面的const去掉。
  6. 返回值不是类的成员变量。
int &Test::GetValue() const
{  
    int temp = value;  
    return temp;  
}  

 

cosnt函数(const放在函数后面):

在调用成员函数时,对象的传递是通过this隐藏的,const实际上修饰了this指向的对象,假如定义了:

int &Test::GetValue() const
{  
    int temp = value;  
    return temp;  
}  

 

  1. f函数其实有两个参数,第一个是A * const thisthis指向的对象是不能改变的) 另一个才是int类型的参数。
  2. 如果我们不想f函数改变参数的值,可以把函数原型改为f(const int),但如果我们不允许f改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是const A *const this
  3. const修饰*this是本质,至于说“表示该成员函数不会修改类的数据,否则会编译报错”之类的说法只是一个现象,根源就是因为*thisconst类型的。

 

在C中,常量只的是字面值,如0x1,1L等,const称为只读常量。
const声明的变量在栈里面,只是其内容不能在代码中改变,它不是在静态内存中的,访问它的时候弹栈。
访问cosnt变量时,不能像真正的常量一样从一个已知的固定内存地址中取得,这对早期的C语言编译器是不可能的。
由于const是定义在栈里面的,它在程序运行时才能确定里面的值是多少,所以在编译的时候不能用它来定义数组大小了 。

 

转载自:http://blog.csdn.net/zcf1002797280/article/details/7816977(侵权即删)

 

posted @ 2016-05-11 09:36  寒风晓月  阅读(293)  评论(0编辑  收藏  举报