沒有學不會的C++:用戶自定義的隱式類型轉換

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 指針
複製代碼

用戶自定義的隱式轉換是本篇的重頭戲,通常咱們說自定義隱式轉換,指兩方面內容:函數

  1. 利用接受單個參數的構造函數,能夠將其餘類型的對象轉換爲本對象
  2. 使用類型轉換函數, 將本類的對象轉換爲其餘對象

二者合起來能夠構成一個雙向轉換關係,下面咱們看一個例子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 點

  1. 避免定義看上去不符合預期的轉換,例如將 string 轉換爲一個 dog
  2. 避免定義雙向的類型轉換,A 能夠向 B 轉換,但繼續實現 B 向 A 的轉換就過猶不及

固然,隱式轉換並非一無可取,它仍然有存在的意義,下面咱們來舉一個正面的隱式轉換的例子,當你的類是一個處理數字的類型時,例如:有理數類,就可使用隱式轉換技術:

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* 定義了有理數的乘法雲算符。

參考:www.youtube.com/watch?v=S7M…

相關文章
相關標籤/搜索