1. 程式人生 > >C++關鍵字explicit與隱式類型別轉換

C++關鍵字explicit與隱式類型別轉換

最近在翻閱EffectiveC++一書,就邊學邊做筆記了,之前很多東西沒能及時整理上來,當時的想法是害怕自己在閱讀過程中很多東西不能夠理解的很深刻,之後就是不斷的遺忘再遺忘或者隨著時間的推移就不想去碼字了,時間真是個可怕的東西,年齡大了就開始害怕了。


C++術語(Terminology)

1.宣告式(declaration)

告訴編譯器某個東西的名稱和型別(type),但略去細節。

for example

extern int x;    //物件(object)宣告式
std::size_t numDigits(int number);   //函式(function)宣告式
class Widget;    //類(class)宣告式

template<typename T>    //模板(template)宣告式
class GraphNone;  //"typename"的使用見條款42

2.簽名式(signature)

也就是引數和返回型別。一個函式的簽名等同於該函式的型別。

numDigits函式的簽名是std::size_t (int),也就是說“這函式獲得一個int並返回一個std::size_t”。

3.定義式(definition)

定義式的任務是提供編譯器一些宣告式所遺漏的細節。

對物件而言,定義式是編譯器為此物件撥發記憶體的地點。

對function或function template而言,定義式提供了程式碼本體。

對class 或 class template 而言,定義式列出它們的成員。

int x;                            //物件的定義式
std::size_t numDigits(int number) //函式的定義式
{                                 //此函式返回引數的數字個數
	std::size_t digitsSoFar = 1;  //例如十位數返回2,百位數返回3
	
	while ((number /= 10) != 0) ++digitsSoFar;
	return digitsSoFar;
}

class Widget{                    //class定義式
	public:
	Widget();
	~widget();
	...
};

template<typename T>            //template的定義式
class GraphNode {
	public:
	GraphNode();
	~GraphNode();
	...
};

4.初始化(initialization)

是“給予物件初始值”的過程。對使用者自定義型別的物件而言,初始化由建構函式執行。所謂default建構函式是一個可被呼叫而不帶任何實參者。這樣的建構函式要不沒有引數,要不就是每個引數都有預設值:

class A{
	public:
	A();                      //default建構函式
};

class B{
	public:
	explicit B(int x = 0, bool b = true);   //default建構函式
};							

class C{
	public:
	explicit C(int x);        //不是default建構函式
}
5.explicit關鍵字和隱式型別轉換(implicit type conversions)

一篇部落格講的比書上詳細特摘錄如下:(原文地址點選開啟連結

//----------------------------------------------------------------------------------------------------------------

for example: 1.1

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定義一個類,名字叫Fruit
{
 string name;     //定義一個name成員           
 string colour;   //定義一個colour成員
 
public:
 bool isSame(const Fruit &otherFruit)   //期待的形參是另一個Fruit類物件,測試是否同名
 {
  return name == otherFruit.name;
 }
 void print()              //定義一個輸出名字的成員print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst,const string &cst = "green"):name(nst),colour(cst){}  //建構函式
 
 Fruit(){}
};

int main()
{
 Fruit apple("apple");
 Fruit orange("orange");
 cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl;  //沒有問題,肯定不同
 cout<<"apple = /"apple/" ?:"<<apple.isSame(string("apple")); //用一個string做形參?
 
    return 0;
}

你會發現最後的使用上,我們用一個string型別作一個期待Fruit類形參的函式的引數,結果竟然得出了是true(1),不要感到奇怪,這就是我現在要講的東西,隱式類型別轉換:“可以用單個實參來呼叫的建構函式定義了從形參型別到該型別的一個隱式轉換。”(C++ Primer)首先要單個實參,你可以把建構函式color的預設實參去掉,也就是定義一個物件必須要兩個引數的時候,檔案編譯不能通過。然後滿足這個條件後,系統就知道怎麼轉換了,不過這裡比較嚴格:)以前我們構造物件的時候Fruit apple("apple")其實也已經有了一個轉換,從const char *的C字串格式,轉為string,在這裡,你再apple.isSame("apple")的話,蠢系統不懂得幫你轉換兩次,所以你必須要用string()來先強制轉換,然後系統才知道幫你從string隱式轉換為Fruit,當然其實你自己也可以幫他完成。cout<<"apple = /"apple/" ?:"<<apple.isSame(Fruit("apple"));這樣。參考例子1.2 :Fruit apple = Fruit("apple");  //定義一個Fruit類物件apple。也就是這樣轉換的。不過這就叫顯式轉換了,我們不標出來,系統幫我們完成的,叫隱式的唄。這裡要說的是,假如你顯示轉換就可以不管有多少引數了,比如在前面提到的必須需要兩個引數的建構函式時的例子。

1.2

#include <string>
#include <iostream>
using namespace std;
class Fruit               //定義一個類,名字叫Fruit
{
 string name;     //定義一個name成員           
 string colour;   //定義一個colour成員
 
public:
 bool isSame(const Fruit &otherFruit)   //期待的形參是另一個Fruit類物件,測試是否同名
 {
  return name == otherFruit.name;
 }
 void print()              //定義一個輸出名字的成員print()
 {
  cout<<colour<<" "<<name<<endl;
 }
 Fruit(const string &nst,const string &cst):name(nst),colour(cst){}  //建構函式
 
 Fruit(){}
};

int main()
{
 Fruit apple("apple","green");
 Fruit orange("orange","yellow");
 cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl;  //沒有問題,肯定不同
 cout<<"apple = /"apple/" ?:"<<apple.isSame(Fruit("apple","green")); //顯式轉換 
    return 0;
}

在你不想隱式轉換,以防使用者誤操作怎麼辦?C++提供了一種抑制建構函式隱式轉換的辦法,就是在建構函式前面加explicit關鍵字,你試試就知道,那時你再希望隱式轉換就會導致編譯失敗,但是,要說明的是,顯式轉換還是可以進行,出於不提供錯誤原始碼例子的原則,錯誤的情況就不提供了,自己試試吧:)在說這個東西之前,我還不懂,現在我懂了:)我現在好像都習慣邊學邊講了,有什麼錯誤,你可要指出來啊。

//------------------------------------------------------------------------------------------------------------

被宣告為explicit的建構函式通常比其non-explicit 兄弟更受歡迎,因為它們禁止允許編譯器執行非預期(往往也不被期望)的型別轉換。

好的,到此結束了!