1. 程式人生 > >effective c++條款24:若所有引數皆需要型別轉換,請為此採用non-member函式

effective c++條款24:若所有引數皆需要型別轉換,請為此採用non-member函式

考慮一個有理數的類:

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;
}