zoukankan      html  css  js  c++  java
  • 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(侵权即删)

  • 相关阅读:
    一则自用iptables例子解释
    SVN配置钩子文件限制提交文件时必须填写更新日志
    Nginx反向代理配置配置实例
    mysql 物理数据存放
    VisualVM、JConsole
    Rabbitmq
    pdm画表间结构
    tomcat jvm 参数优化
    【转载】Java导入导出excel
    【转载】使用 Google Guava 美化你的 Java 代码
  • 原文地址:https://www.cnblogs.com/pukaifei/p/5480780.html
Copyright © 2011-2022 走看看