const與指針,引用的結合

const 這個關鍵字會將所修飾的表達式置爲常量,編譯器會盡全力保證該表達式不可被修改,在const不和指針或者引用結合是,數據的類型是無關const的,可是若是一但和指針和引用相結合,那麼考慮數據類型時候必需要考慮const了指針


class Test
{
public:
    Test(int data = 10):_ptr(new int(data)){}
    ~Test(){
        delete _ptr;
    }

    int GetInt(){
        return *_ptr;
    }
    int GetInt()const{
        return *_ptr;
    }

    int& GetIntRef(){
        return *_ptr;
    }
    int& GetIntRef()const{
        return *_ptr;
    }

    int* GetIntPtr(){
        return _ptr;
    }
    int* GetIntPtr()const{
        return _ptr;
    }  //  int *const _ptr

    int*& GetIntPtrRef(){
        return _ptr;
    }
    int*const& GetIntPtrRef()const{
        return _ptr;
    }
private:
    int *_ptr;
};

int main()
{
    Test t1;
    const Test t2;

    int a1 = t1.GetInt();
    int &b1 = t1.GetInt();
    const int &c1 = t1.GetInt();

    int a2 = t2.GetInt();
    int &b2 = t2.GetInt();
    const int &c2 = t2.GetInt();

    return 0;
}

舉個例子,上面代碼種有幾處是有錯誤的,找出它~~~~

上面代碼種b1是錯誤的,b2也是
先來看看b1,getInt返回的是一個整形值,它所返回的是一個當即數,那麼用引用接收的時候就會出現沒法給當即數取址的問題,此時寄存器帶出的是一個當即數,而當即數不會有地址的,因此若是要引用,必須是一個常引用,不然編譯不經過。
b2也是一樣的,當即數不能取址code


那若是一開始返回的不是當即數而是一個引用呢?

int main()
{
    Test t1;
    const Test t2;

    int a1 = t1.GetIntRef();
    int &b1 = t1.GetIntRef();
    const int &c1 = t1.GetIntRef();

    int a2 = t2.GetIntRef();
    int &b2 = t2.GetIntRef();
    const int &c2 = t2.GetIntRef();

    return 0;
}

這樣編譯下來,並無什麼問題,緣由是返回一個引用,此時會生成一個臨時量,不存在當即數的問題了對象


那麼咱們再來看看若是要是返回一個指針呢?

int main()
{
    Test t1;
    const Test t2;

    int *a1 = t1.GetIntPtr();
    int *&b1 = t1.GetIntPtr();
    const int *&c1 = t1.GetIntPtr();

    int *a2 = t2.GetIntPtr();
    int *&b2 = t2.GetIntPtr();
    const int *&c2 = t2.GetIntPtr();

    return 0;
}

此時又出現了剛纔的問題,指針自己也是一個在32位系統下4字節的基礎類型,因此返回值也會是一個當即數,不能取址,必須用常引用內存


簡單總結一下:
對於賦值表達式的兩邊get

  1. 若是是值,只須要類型相同
  2. 若是是指針,要求必須能取地址
  3. 若是是引用,必須是能取地址,類型相同

再補充3個例子編譯器

int*& GetIntPtrRef(){
        return _ptr;
    }
    int*const& GetIntPtrRef()const{
        return _ptr;
    }

int main()
{
    Test t1;
    const Test t2;

    int *a1 = t1.GetIntPtrRef();
    int *&b1 = t1.GetIntPtrRef();     
    const int *&c1 = t1.GetIntPtrRef();

    int *a2 = t2.GetIntPtrRef();
    int * &b2 = t2.GetIntPtrRef();
    const int *&c2 = t2.GetIntPtrRef();

    return 0;
}

此時編譯下來有3個錯誤,本質上仍是類型不相同致使的,GetIntPtrRef()const的返回值是int const,可是b2是一個int *&,因此須要再&前面加上const保持類型相同
c2也是同樣的道理,須要再引用前面加const編譯

這裏還有一個c1的錯誤,這個錯誤有些特殊,前面說過,const修飾的變量,編譯器會盡力保證這個值不可被修改,此時const修飾的是&c1這個表達式,因此經過c1是不能修改的,可是此時卻能夠經過c1這個引用變量修改c1的內存,將引用換個指針就更加明顯了,q,p這兩個變量均可以修改同一片內存,此時咱們告訴編譯器,q這是個常量,不許修改了,可是編譯器發現經過*p竟然也能夠修改這裏的內存,必然是經過不了的。修改的話能夠限定常引用,防止修改同一片內存class

const int *const &c1 = t1.GetIntPtrRef();

再看第二個

int ** GetIntPtrPtr()
{
    return &_ptr;
}
int *const* GetIntPtrPtr()const{
    return &_ptr;
}

int main()
{
    Test t1;
    const Test t2;

    int **a1 = t1.GetIntPtrPtr();
    int **b1 = t1.GetIntPtrPtr();
    const int **c1 = t1.GetIntPtrPtr();

    int **a2 = t2.GetIntPtrPtr();
    int **b2 = t2.GetIntPtrPtr();
    const int **c2 = t2.GetIntPtrPtr();

    return 0;
}

此時有4個錯誤,再常對象t2中_ptr是一個常量不可被修改的,因此 t2全部接收的類型都是int const ,故而a2,b2,c2都有錯誤
而c1中也就是那個特殊的錯誤,const修飾的變量編譯器會盡力防止內存修改,因此應該是const int const 類型,故而c1也有錯誤基礎


再來看看最後一個例子

int ** const& GetIntPtrPtrRef(){
    return &_ptr;
}
int *const * const& GetIntPtrPtrRef()const{
    return &_ptr;
}

int main()
{
    Test t1;
    const Test t2;

    int **a1 = t1.GetIntPtrPtrRef();
    int **b1 = t1.GetIntPtrPtrRef();
    const int **c1 = t1.GetIntPtrPtrRef();

    int **a2 = t2.GetIntPtrPtrRef();
    int **b2 = t2.GetIntPtrPtrRef();
    const int **c2 = t2.GetIntPtrPtrRef();

    return 0;
}

這個的錯誤和上個例子十分類似,在此很少贅述了。變量


總結:

  1. 等式的兩邊先進行類型的比較,須要注意const在右邊沒有*時候是不計算類型的,保證兩邊的類型首先相同。
  2. 其次注意const修飾的變量,指針或者引用是必需要求參數可以取地址的,若是是一個當即數的話,就必須注意要用常引用。
  3. 再就是那個特殊的錯誤,對於const與二級指針的結合,要求const修飾的對象的那塊內存不能修改,因此兩個*以前要麼都有const,要麼都沒有。
相關文章
相關標籤/搜索