effective c++條款24:若所有引數皆需要型別轉換,請為此採用non-member函式
阿新 • • 發佈:2018-11-05
考慮一個有理數的類:
class Rational
{
public:
Rational(int nc = 0, int dc = 1):n(nc), d(dc){}
~Rational(){}
private:
int n, d; //n為分子,d為分母
};
我們想要實現諸如*、+、-等操作,但不知道是應該實現一個non-member函式,member函式,還是一個non-member friend函式,此時,秉著面向物件精神,我們一般首先會實現一個member函式:
class Rational { public: Rational(int nc = 0, int dc = 1):n(nc), d(dc){} ~Rational(){} private: int n, d; //n為分子,d為分母 public: const Rational operator *(const Rational &rhs) { Rational Temp(this->n * rhs.n, tjis->d * rhs.d); return Temp; } };
我們發現按如下方法呼叫會出現問題:
乍一看一臉懵逼,乘法不是應該滿足交換律嗎?將乘法以函式形式表示,就會發現問題所在:
Rational b = a.operator *(3);
Rational c = 3.operator *(a);
顯而易見,3裡面怎麼可能調用出operator *呢。。。
雖然第一句通過了編譯,但是有人會想operator *需要的引數是一個Rational,可是3明明是一個整數,發生了什麼呢?
答案就是隱式型別轉換。
編譯器自然知道operator *的引數,所以它做了一件事:
const Rational Temp(3); Rational b = a * Temp;
也就相當於將3隱式轉換為一個Rational。
那麼為什麼第一句能隱式轉換,第二句就不行呢?
原因就是“只有當引數被列於引數列內”,這個引數才能被隱式轉換。
Rational b = a.operator *(3);
Rational c = 3.operator *(a);
顯然,第一句中,3在引數列中,而第二句並不在。
所以為了使*號兩邊都能進行隱式轉換,我們可以去掉operator *的隱式引數this:
class Rational { public: Rational(int nc = 0, int dc = 1):n(nc), d(dc){} ~Rational(){} private: const int n, d; //n為分子,d為分母 public: friend const Rational operator *(const Rational &lhs, const Rational &rhs) { Rational Temp(lhs.n*rhs.n,lhs.d*rhs.d); return Temp; } };
這樣,兩句話就都可以正常編譯了,但此時,friend函式破壞了class的封裝性,所以為了滿足封裝性,我們可以將其改為non-member non-friend函式:
class Rational
{
public:
Rational(int nc = 0, int dc = 1):n(nc), d(dc){}
~Rational(){}
private:
const int n, d; //n為分子,d為分母
public:
int Numerator() const; //分子分母的介面函式
int Denominator() const;
};
const Rational operator *(const Rational &lhs, const Rational &rhs)
{
Rational Temp(lhs.Numerator() * rhs.Numerator(), lhs.Denominator()*rhs.Denominator());
return Temp;
}