【OOP】C++ const成員函數


預備知識

1、代碼轉換分析技巧

在早期某些編譯器會將C++代碼翻譯為C代碼,然后使用C編譯器生成可執行文件。其中翻譯的一個轉化就是:將this指針顯式添加到成員函數的第一個參數位置上,並在成員函數調用時,自動將對象的地址傳遞給參數this。
 
這個過程用如下代碼解釋:
#include<iostream>
#include<cstdio>
using namespace std;

class Dog
{
public:
    Dog(unsigned  en = 0) :energe(en){}  

    void bark() const    
    {
        for (size_t i = 0; i < energe; i++)
            cout << "wang wang!\n";
    }

    void feed(unsigned add)   
    {
        energe += add;
    }

private:
    unsigned  energe;
};

int main()
{

    Dog dog;

    dog.feed(2);   
    dog.bark();    

    return 0;
}

 

翻譯轉換

//原型轉換為C代碼后的樣子
void bark(const Dog*this);
void feed(Dog*this,unsigned add);

//調用時的轉換
dog.feed(2);   ----->  feed(&dog , 2); 
dog.bark();    ----->  bark(&dog);
 
現在的C++編譯器可能不是這樣的工作的,但是,如果你使用這個轉換方式去應用到實際的編程的代碼分析中,很多代碼和語法特性就會迎刃而解。

 

2、頂層const,底層const

頂層const:指針變量本身是常量。(頂層const不適合引用,因為引用天生就是一個常量,始終引用一個對象,直到消亡)

           如   int* const p = &a;

底層const:變量指向(或者引用)的對象被視為常量。(注意這里的用詞:被視為,因為對象不一定就是常量,也可能是底層const指針、引用的一廂情願)

           如    const int*p ;    int const *p;    這二者寫法等價

                  const int& r;

 

一個非底層const指針,它對它指向的對象有 讀、寫的權利,因此,為了確保數據安全,它僅能指向非常量對象。

int a=1;

const int b = 2;

int*p1 = &a;  //OK

p1  = &b;   //ERROR不允許

 

一個底層const指針,他對它指向的對象僅有讀的權利。因此,它可以指向常量和非常量。

int a=1;

const int b = 2;

const int*p1 = &a;  //OK

p1 = &b ;       //OK

 

 

3、函數的參數是否為 底層const 可以作為重載的依據

舉例說,下面2個函數可以重載。

void foo(const int*p);     //A
void foo(int*p);           //B

編譯器在重載解析時,根據傳遞的參數來判斷調用哪一個版本。

當僅存在版本A時,若傳遞的是非常量int的指針,或常量int的指針,都可以成功調用。

當僅存在版本B時,若傳遞的是非常量int的指針,可以成功調用,但不允許傳遞常量int的指針。

當A、B 都存在,重載時,若傳遞的是非常量int的指針,則優先使用版本B,因為這是最匹配的。

                                   若傳遞的是常量int的指針, 則使用版本A。

 

另外,參數本身是否是const 不作為重載的依據,下面的不能重載。

void foo(const int a);
void foo(int a);
//C語言中,參數修飾為const和不使用const修飾 被編譯器一樣對待,C++為兼容C,也使用了這個策略。因此二者等價。

 

如果一個成員函數在邏輯上不會修改對象的狀態(字段),就應該定義為const函數

 

在上面的Dog類代碼中,如果去掉bark函數后的const修飾符,並試着用一個const 對象去調用bark函數,則發現編譯器報錯。

void bark() 
{
     for (size_t i = 0; i < energe; i++)
         cout << "wang wang!\n";
}

/////////////////////////
const Dog dog;

dog.bark(); //錯誤提示為類型不兼容

 

拋開代碼,從業務邏輯上來看,bark函數只是在屏幕上輸出消息,根本不會改變對象的狀態,那即便是const 對象,也必須能成功調用啊。

這里報錯顯然是bark函數的問題:應該定義為const函數,這點大家都是很清楚的。但是為什么要這樣的呢?

按照最開始介紹的分析方法,發生錯誤的代碼等價與下面的C代碼。

const Dog dog;

bark(&dog)      //而bark函數轉化后的原型為:void bark(Dog*this);

顯然,將常量的指針賦值給非常量指針是不允許的。

 

再比如,C++標准庫中的string類,它的用於獲取字符串長度的成員函數都是const修飾的。如果不是這樣的話,那么字符串常量就不能獲取他們的長度了,這簡直荒謬!

size_type size() const; size_type length() const; 

 


const成員函數可以形成重載

成員函數同時存在 const版本和非const版本?可以重載?是的。

同樣,先按照最開始介紹的第1條分析方法,轉換為C代碼。然后根據第3條的分析就可以了。這里不再贅述。

 

最常用的就是,按照約定當重載索引運算符 [ ] 時,會同時編寫一個 const版本和非const版本。

例如std::vector的[ ]運算符函數

reference       operator[]( size_type pos );
        
const_reference operator[]( size_type pos ) const;

 

總結

1、如果一個成員函數在邏輯上不會修改對象的狀態(字段),就應該定義為const函數

 

2、

如果對象是const,則它只能調用const成員函數。

如果對象是普通的非const對象:

                                        調用的某個成員函數是非const函數,則理所當然調用它。

                                        調用的某個成員函數是const函數,則當然也可以調用他。(底層const指針可以指向非常量對象)

                                        調用的某個成員函數同時存在 const版本和非const版本,則優先調用非const成員函數,編譯器總是使用最匹配的版本。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM