【24】若所有引數皆需型別轉換,請為此採用non-members函式

Andy Niu發表於2014-01-23

1、令class支援隱式型別轉換,往往是個糟糕的主意。但有些情況是合理的,比如數值型別。考慮,有理數Rational有分子,分母兩個欄位,預設引數值為0,1。Ration a = 2;我們期望構造一個分子為2,分母為1的有理數,這是非常合理和自然的。因此,Rational的構造方法為Ration(int numerator =0, int denominator =1);不新增explicit。

2、考慮Rational 有個成員方法 operator*,如下:

  const Ration operator*(const Rational& rhs) const;

  Rational a (1,2);

  Rational result;

  result = a*2; // OK

  result = 2*a; // Error

  為什麼?

  對於a*2,2會隱式轉化為Rational,而對於2*a,2不會隱式轉化為Rational。這種情況下,編譯器嘗試查詢non-member方法的呼叫,即operator(2,a); 在當前名稱空間或者全域性global作用域查詢不到,報錯。

3、分析下,為什麼對於2*a,2不會隱式轉化為Rational。

  呼叫方法的時候,如果沒有完全匹配的方法,編譯器嘗試進行一次隱式型別轉換,使之與方法匹配成功。可認為編譯器做了一次適配的過程,實參與形參型別不一致,把實參轉化為形參的型別,從而匹配成功。

  對於a*2,2對應形參,而對於2*a,2對應this指標常量。不能隱式轉化為this指標。再接著思考,為什麼不能轉化為this指標?

  假如可以隱式轉化為this指標,那麼有成千上萬的類(沒有宣告explicit構造方法),當呼叫2*a的時候,編譯器必須遍歷每一個類,檢視這個類中是否有* Rational的成員方法,顯然不可能。

4、另外,還有一點,隱式型別轉換隻能進行一次,不能進行多次。也就是說,2 -> XXX -> Person是不可行的。思考下,為什麼?

  假如隱式型別轉換允許多次,就意味著,從2 到Person的轉換過程,編譯器必須查詢出所有可能的轉換路徑,這顯然不切實際。即使,找到了所有的轉換路徑,那麼,存在多個轉換路徑,到底使用哪一個呢?

5、怎麼解決上面的問題呢?  即2*a。

  既然不能隱式轉換為this指標,那麼就是用non-member方法。這樣的話,無論對於哪個形參需要隱式型別轉化,都可以。編譯器從當前名稱空間或者全域性作用域,查詢operator*(Rational ,Rational),這種形式的方法畢竟很有限。

6、定義的non-member方法,是否需要宣告為Rational的friend方法?

  根據經驗,儘量避免使用friend,為啥?因為friend破壞了封裝。因此,如果不需要訪問Rational的private成員,就不要宣告為friend。

相關文章