標籤:

c++不同類的對象之間自動類型轉換有什麼用?

今天在看《C++編程思想》,裡面第14章第6節在講非自動繼承的函數的時候,用了一個例子,其中說到了類的自動類型轉換的問題。為了討論更清楚,我另外寫了一段代碼,請各位看看,問題還是一樣的:類的自動類型轉換有什麼用?這樣設計的目的是什麼?

//: C14: AutoConvert.cpp
//a test for auto type convertion
#include &
using namespace std;

class A{
public:
A() { cout &<&< "A::A()" &<&< endl; } ~A() { cout &<&< "A::~A()" &<&< endl; } }; class B{ public: B() { cout &<&< "B::B()" &<&< endl; } B(const A) { cout &<&< "B::B(const A)" &<&< endl; } ~B() { cout &<&< "B::~B()" &<&< endl; } void showB() { cout &<&< "showB()" &<&< endl; } }; void f(B bb) { bb.showB(); } int main() { A a; f(a); return 0; }

運行程序的列印如下:

A::A()

B::B(const A)

showB()

B::~B()

A::~A()

從列印來看,調用f(a)時調用了,B(const A)這個函數,可以理解為重載的構造函數。我的疑問:

1、全局函數f(B bb)明明要接收一個B的對象作為參數,為什麼要傳一個A的對象給他?

2、f()調用的結果是,產生了一個B的對象bb(雖然是作為參數),而對象a依然存在(因為最後才調A的析構函數),為什麼要把這種方式叫做「自動類型轉換」呢?在我看來,自動類型轉換應該是把一個對象從一種類型轉換為另一種類型?

3、這樣設計的目的是什麼?哪些地方用了這個特性?


這主要還是C的殘餘勢力。

C不是strong type,所以C裡面有一票五光十色的implicit cast,不光有int &<-&> long之類還算說得過去的,而且還有long &<-&> pointer之類說不過去的。

C++當時的設計目標之一就是自定義類型要和內置類型有同等地位,所以內置類型能幹的臟活自定義類型也得支持,所以就有了cast constructor和cast operator這些東西。

現在想想其實當年把implicit cast整個砍掉說不定會更好點。


這設計太有用了好嗎

舉個例子,我定義了幾個類,分別是utf8string,utf16string,utf32string,gb18030string。

寫好自動類型轉換的方法,再重載操作符=

這樣我就能從一種編碼的字元串直接構造另一種,還能直接在不同編碼的字元串直接互相賦值,多方便


1、全局函數f(B bb)明明要接收一個B的對象作為參數,為什麼要傳一個A的對象給他?

為了告訴你還有這麼個功能。

2、f()調用的結果是,產生了一個B的對象bb(雖然是作為參數),而對象a依然存在(因為最後才調A的析構函數),為什麼要把這種方式叫做「自動類型轉換」呢?在我看來,自動類型轉換應該是把一個對象從一種類型轉換為另一種類型?

因為你的a是一個變數,他不能轉換後就幹掉啊,不然後面的代碼還怎麼用這個a?如果你寫的是f(A()),那又不一樣。

3、這樣設計的目的是什麼?哪些地方用了這個特性?

譬如說需要std::string的時候你傳一個const char*進去。


自動類型轉換的作用就是好看。比如一個函數:

void f (B b);

然後有一個類 A,你希望讓 f 能用在下面的代碼里:

A a;

f(a);

當然前提是從 A 到 B 有合理的對應。其實你完全可以寫成:

A a;

f(a_to_b(a));

這樣的代碼非常清晰。

但是 C++ 的設計者認為這種 explicit 的代碼不好看。於是他就拚命的加 code cypher 的 feature。直到每一個人都能寫出自己認為夠好看的代碼。其實沒什麼用處。


這個現在似乎都叫隱式類型轉換。按造C++標準,隱式類型轉換隻能有一層。

而且這個其實不是類型轉換,而是構造了一個B類型的臨時對象,構造函數用了B(const A)。

這個功能在大量使用模板的時候,對類型的判定和包裝的時候特別有用。比如std::bind和std::ref配合使用


「3、這樣設計的目的是什麼?哪些地方用了這個特性?」

有了這個特性:

void f(const string a);的調用可以從f(string("abc")變成f("abc")

使用到這個特性的相關代碼還有stream自動轉換為bool

std::ifstream is;
is.open ("test.txt");
if (is) {
// read file
}

智能指針轉換為bool,等等

當然標準庫里的這個轉換時通過定義 operator bool來實現的,而不是你的構造函數做的

「1、全局函數f(B bb)明明要接收一個B的對象作為參數,為什麼要傳一個A的對象給他?」

為了讓代碼更加簡潔,省去手工轉換的代碼,例如f("abc")比f(string("abc"))要簡潔,可讀也更好

「2、f()調用的結果是,產生了一個B的對象bb(雖然是作為參數),而對象a依然存在(因為最後才調A的析構函數),為什麼要把這種方式叫做「自動類型轉換」呢?在我看來,自動類型轉換應該是把一個對象從一種類型轉換為另一種類型?」

那麼請問你是怎麼轉換呢?難道像現實世界一樣,蛋變成雞之後,蛋自然就沒啦?計算機的世界不是這樣的。你寫了一篇word,但是我要一個ppt格式的,那麼你會根據word生成一個ppt,而不是把word變成ppt吧


這個例子中的構造函數是一種類型轉換構造函數,它的目的在於實現類型的自動轉換,它的特點是只有一個參數但不是複製構造函數。在編譯的時候編譯系統會自動調用類型轉換構造函數建立一個臨時對象或臨時變數,你給的這段代碼並沒有明確給出類型轉換構造函數的用法,我這邊舉個栗子:

// 構建一個複數類
class Complex{
public:
double real;
double imag;
Complex(double r, double i){
real = r;
imag = i;
}
Complex(int i){// 類型轉換構造函數
real = i;
imag = 0;
}
};

int main(){
Complex a(1, 2);
a = 8; // 8在此處被自動轉換成一個臨時的Complex對象並賦值給a
}

1、全局函數f(B bb)明明要接收一個B的對象作為參數,為什麼要傳一個A的對象給他?

的確傳了B的對象給它啊,編譯的時候系統自動調用類型轉換構造函數建立一個臨時B對象傳到了函數f中。

2、f()調用的結果是,產生了一個B的對象bb(雖然是作為參數),而對象a依然存在(因為最後才調A的析構函數),為什麼要把這種方式叫做「自動類型轉換」呢?在我看來,自動類型轉換應該是把一個對象從一種類型轉換為另一種類型?

轉換成另外一種類型了,但是中間過程沒有展示給你看。

3、這樣設計的目的是什麼?哪些地方用了這個特性?

見栗子。


類設計者,和使用者,要區分對待啊。


本質上程序的執行是為了運算,而不同類型之間不能進行運算,所以運算前必須要轉換成相同類型…所以但凡與需要的類型不同時,都需要轉換…


推薦閱讀:

應該以什麼心態和標準來學《 c++primer 》和 c++ ?
C、C++、MATLAB、Python、Go 哪個比較適合寫演算法?
C/C++該採用怎樣的命名規則才能讓自己的代碼足夠清晰呢?
Visual Studio有哪些不為人知卻特別好用的實用工具/技巧/功能/特性?
C++中有哪些設計精良的部分(精華),還有哪些是不值得花費很多時間探究的知識點?

TAG:C |