天天看點

const相關用法(Effective C++_3)

一、構造操作和指派操作的差別

如果一個新對象被定義,一定會有一個構造函數被調用,不可能調用指派操作;如果沒有新對象定義,就不會有構造函數調用,那麼就隻能調用指派操作;

class Demo;
Demo w1;//調用預設構造函數
Demo w2(w1);//調用複制構造函數
w1=w2;//調用指派操作

Demo w3=w2//調用複制構造函數
           

二、盡量使用const,enums代替宏(Effective C++_2)

三、const的用法(Effective C++_3)

1、常變量

const float PI=;    //定義了常變量PI
const int Number_of_Student=; //定義了常變量Number_of_Student
           

常變量必須隻能在說明時進行初始化;常變量初始化之後,不允許再被指派;常變量必須先說明後使用。

2、引用使用const

(1)const引用是指向const對象的引用。

const int i = ; 
const int &ref = i; 
           

可以讀取ref,但不能修改。這樣做是有意義的,因為i本身就不可修改,當然也不能通過ref來修改了。是以也就有将const變量指派給非const引用是非法的。

(2)非const引用是指向非const類型變量的引用。

const引用可以初始化為不同類型的對象或者右值(如字面值常量),但非const引用不可以。

// legal for const references only 
int i = ; 
const int & ref = ; 
const int & ref1 = r + i; 
double d = ; 
const int &ref2 = d; //注意這裡的類型不同
           

以綁定到不同類型的ref2為例解釋原因,編譯器會把ref2相關的代碼轉換如下:

int temp = d; 
const int &ref2 = temp; // bind ref2 to temporary 
           

ref2實際上是綁定到一個臨時變量上,如果ref2不為const,那麼按道理就可以通過修改ref2而修改d的值,但實際上d并不會改變。是以為了避免這個問題,ref2隻能是const。

非const引用隻能綁定到與該引用同類型的對象,const引用則可以綁定到不同但相關的類型的對象或綁定到右值。

2、指針使用const

const出現在*的左邊,表示所指之物是常量,出現在右邊,表示指針本身是常量,如果出現在兩邊,表示所指之物和指針本身均是常量

const char* p="greeting";//所指物是常量,不可改變
char* const p="greeting";//指針本身是常量
const char* const p="greeting"//所指物和指針本身均是常量
           

注意:

const char* p="greeting";與下面的等價
char const* p="greeting"
           

3、疊代器使用const

假如你希望疊代器所指之物不可改變,你需要使用const_iterator:

vector<int> vec;
......
vector<int>::const_iterator iter=vec.begin();
*iter=;//錯誤,*iter是個常量
iter++;//可以
           

如果你希望疊代器本身不改變,你需要使用const,聲明疊代器為const就像聲明指針為const一樣(T* const),表示這個疊代器不得指向其他東西,但是其所指之物可以發生改變

vector<int> vec;
......
const vector<int>::iterator iter=vec.begin();
*iter=;//可以
iter++;//錯誤,iter是個const
           

4、函數聲明,包括函數傳回值、參數使用const

(1)函數參數使用const,可以保證傳入的值,在函數内部操作時,不改變原有的值

(2)函數的傳回值為const

可以防止不适當的操作,比如

class Ration{};
const Ration operator*(const Ration& lhs,const Ration& rhs);
......
Ration a,b,c;
.......
if(a*b=c)
{
......
}//編譯錯誤
           

其實上面是想做一個比較操作,但是由于手誤,寫成了指派操作,那麼在編譯的時候就會不能通過,能找出這個錯誤

5、成員函數使用const

(1)const用于成員函數是為了,該成員函數能作用于const對象上

class Demo{
void print(){cout<<"success"}
};
......
Demo obj;
obj.print()//正确,可以調用
const Demo obj2;
obj2.print()//錯誤,不能調用
           

假如,把上面函數聲明為const

void print()const{cout<<"success"}
......
obj2.print()//正确,可以調用
           

(2)const成員函數重要的原因

第一:可以使類的接口比較容易了解,因為可以知道,哪個函數能改動對象内容,哪個函數不能改動(const成員函數内部,不能改動對象内容,非const成員函數内可以改變);

第二:使操作const對象成為可能,正如條款20所述,改善C++程式效率的一個辦法是使用傳指向常量的引用或指針,而這一計算的前提是,我們擁有const成員函數來處理取得的const對象;

下面代碼可以展現這一點:

class TextBlock{
const char& operator[](size_t position)const{
return text[position];
}
private:
string text;
};
......
void print(const TextBlock& ctb){
cout<<ctb[];//傳進來的參數是const對象,隻有const成員函數才能處理,而operator[]是const成員函數
}
           

(3)成員函數僅常量性不同,也可以被重載(不包括傳回值是否為常量)

考慮以下class:

class TextBlock{
const char& operator[](size_t position)const{
return text[position];
}
char& operator[](size_t position){
return text[position];
}
private:
string text;
};
......

TextBlock tb("hello");
cout<<tb[];\\調用非const成員函數operator[]

const TextBlock ctb("hello");
cout<<ctb[];\\調用const成員函數operator[]
           

再考慮以下例子

cout<<tb[];\\正确,讀一個非const對象
tb[]='x';\\正确,寫一個非const對象
cout<<ctb[];\\正确,讀一個const對象
ctb[]='x';\\錯誤,ctb[]傳回類型是const
           

注意,

ctb[0]='x'

錯誤的原因隻因傳回類型所緻,調用[]操作符本身無問題;同時也要注意,非const的operator[]這個版本傳回類型是char&,并不是char,如果是char,那麼

tb[0]=’x’`

無法通過編譯,那是因為,如果函數的傳回類型是個内置型,那麼改動函數的傳回值從來不合法;

(4)編譯器強制實作bitwise constness,但怎麼實作概念上的constness

對于const修飾的成員函數分為兩種觀點: logic constness and bitwise constness.

bitwise constness:它不更改對象的任一bit,即const成員函數不更改對象内的任何非靜态成員變量;

不幸的是,很多不遵守bitwise constness定義的成員函數也可以通過bitwise測試。特别是,一個“修改了指針所指向的資料”的成員函數,其行為顯然違反了bitwise constness定義,但如果對象中僅包含這個指針,這個函數也是bitwise const的,編譯時會通過。這種情況,是反直覺的,考慮以下代碼

class CTextBlock
{
public:
......
 char& operator[](std::size_t position)const//bitwise constness聲明,但其實不合适     
   {return pText[position];}
private:
   char* pText;
};
我們可以這麼做:
const CTextBlock cctb("hello");
char* pc = &cctb[];
*pc = 'J'          //現在cctb為"Jello"
           

雖然operator[]實作的代碼并不改變對象的成員變量ptext,但是通過指針我們終究是改變了其對象的内容;

這種情況,推出來了logical constness,這一派認為,一個const成員函數可以修改它所處理的對象内的bit,但隻在用戶端偵測不出的情況下才得如此;

衆所周知,我們在const成員函數中不能修改成員變量,但當需要修改時就需要另外一個關鍵字—mutable. mutable釋放掉non-static成員變量的logical constness限制(編譯器認定的,即在const内不能修改成員變量,這時就需要mutable;考慮以下代碼:

class CTextBlock{
public:
    int length()const;
private:
    char* pText;
    mutable int textLength;
    mutable bool lengthIsValid;
};
int CTextBlock::length()const
{
    if(!lengthIsValid)
    {
        textLength = std::strlen(pText);
        lengthIsValid = true;
    }
    return textLength;

}
           

由于length是const成員函數,是以在内部改變成員變量textLength、lengthIsValid是不合法的,但是單我們在成員變量名前加上mutable,在const成員函數内部改變成員變量,編譯器就可以通過;

(5)在非const成員函數内調用const成員函數,避免代碼過度重複

class TestBlock
{
private:
string text;
public:
...
const char& operator[](size_t position) const
{
…
return text[position];
}

char& operator[](size_t position)
{
…//操作跟上面相同
return text[position];
}
};
           

從上面代碼中可以看出,代碼的重複度十分高,為了避免這中情況,我們可以在非const成員函數内調用const成員函數,來實作非const成員函數,這樣就可以避免代碼的重複性;下面代碼實作:

char& operator[] (size_t position)
{
return const_cast<char&>(
static_cast<const TestBlock&>(*this)[postion]
);
}
           

上述代碼有兩個轉型動作,(*this)轉換為const TestBlock類型,假如不轉換,編譯器會調用非const成員函數,即自己本身,這樣是個死循環;第二次是将傳回類型轉換為非常量char&,const_cast可以去除常量性;

注意:不能在const成員函數内實作非const成員函數,即上述避免代碼重複性的操作,不能反向進行

參考:Effective C++ 3rd(侯捷譯)、C++ Prime 4rd

注:轉載請注明出處http://blog.csdn.net/zhangchen1003/article/details/48212693

繼續閱讀