C++ 中的類型轉換包含內建類型的轉換和用戶自定義類型的轉換,而這二者都又可分爲隱式轉換和顯示轉換,因此一共有以下四象限表格中的 A、B、C、D 四種狀況java
隱式轉換 | 顯示轉換 (casting) |
|
---|---|---|
內建類型轉換 (int, float ...) |
A | B |
用戶自定義類型轉換 (類 vs 類; 類 vs 內建類型) |
C | D |
本篇只討論隱式轉換,內建類型的隱式轉換舉例以下api
char c = 'A';
int i = c; // 將 char 隱式轉換爲 int,又稱 Integral promotion
char* pc = 0; // int 轉換爲 Null 指針
dog* pd = new yellowdog(); // 指針類型轉換,子類 yellowdog 指針轉換爲父類 dog 指針
複製代碼
用戶自定義的隱式轉換是本篇的重頭戲,通常咱們說自定義隱式轉換,指兩方面內容:函數
二者合起來能夠構成一個雙向轉換關係,下面咱們看一個例子spa
class dog {
public:
dog(string name) {m_name = name;}
string getName() {return m_name;}
private:
string m_name;
};
int main() {
string dogname = "dog";
dog d = dogname;
cout << "my name is " << d.getName() << \n";
return 0;
};
複製代碼
上面例子中,dog(string name) {m_name = name;}
有兩層含義,除了構造函數外,它還能夠做爲隱式轉換函數,將 string
對象轉換爲 dog
對象,能夠看到咱們把 dogname
賦給了 dog d
,像這樣的賦值,一般是無心的行爲,並且它觸犯了 is-a 原則。設計
***若是你不想該構造函數具有隱式轉換的特性,你應該使用 explicit
對該函數進行聲明:指針
explicit dog(string) {m_name = name;}
複製代碼
反過來,咱們還能夠定義一個轉換函數,將 dog
對象轉換爲 string
,以下code
class dog {
// ...
operator string () const { return m_name; }
};
複製代碼
這樣,下面的輸出能夠簡化爲:對象
cout << "my name is " << (string)d << \n";
複製代碼
能夠看到,自定義類型的隱式轉換很容易寫出和本意不符的代碼,這些代碼每每容易出錯,很明顯,這樣的設計算不上是好設計,相反,咱們更應遵循這樣的設計原則:接口
當咱們定義一個 api 接口,咱們但願正確的使用這個 api 是一件很容易的事情,且它很難被錯誤使用(理想狀況下,當你錯誤使用一個 api 時,它不能被編譯經過),而定義過多的轉換函數,卻很容易讓咱們的 api 出錯ci
對於隱式類型轉換,應該記住 2 點
固然,隱式轉換並非一無可取,它仍然有存在的意義,下面咱們來舉一個正面的隱式轉換的例子,當你的類是一個處理數字的類型時,例如:有理數類,就可使用隱式轉換技術:
class Rational {
public:
Rational(int numerator = 0, int denominator = 1)
: num(numberator), den(denominator) {}
int num;
int den;
};
Rational operator*(const Rational& lhs, const Rational& rhs) {
return Rational(lhs.num*rhs.num, lhs.den*rhs.den);
}
int main() {
Rational r1 = 23;
Rational r2 = r1 * 2;
Rational r3 = 3 * r1;
}
複製代碼
上面代碼定義了一個有理數類 Rational
,它的構造函數接受 2 個默認參數,分別表明分子和分母,給該構造函數傳遞一個參數時,Rational
具備隱式轉換的特性,因此咱們能夠直接將數字賦值給 Rational
對象,如:Rational r1 = 23;
爲了不雙向轉換,這裏並無定義將 Rational
轉換爲 int
的轉換函數,而當咱們想實現 Rational
對象和 int
之間自由的算術運算時,咱們須要定義全局的操做符重載,如上面的 operator*
定義了有理數的乘法雲算符。