C++基礎教程面向對象學習筆記及心得感悟[圖]

C++基礎教程面向對象學習筆記及心得感悟[圖]
使用友元函數重載算術運算符:
C ++中一些最常用的運算符是算術運算符 - 即加號運算符(+),減運算符( - ),乘法運算符(*)和除法運算符(/)。請注意,所有算術運算符都是二元運算符 - 這意味着它們需要兩個操作數 - 運算符的每一側都有一個操作數。所有這四個運算符都以完全相同的方式過載。
事實證明,有三種不同的方法來重載運算符:成員函數方式,友元函數方式和正常函數方式。在本課中,我們將介紹友元函數的方式(因爲它對大多數二元運算符更直觀)。下一課,我們將討論正常的函數方式。最後,在本章後面的課程中,我們將介紹成員函數的方式。當然,我們還將總結何時更詳細地使用每一個。

C++基礎教程面向對象學習筆記及心得感悟[圖]
使用友元函數重載運算符:
考慮以下的類:
class Cents
{
private:
int m_cents;
public:
Cents(int cents) { m_cents = cents; }
int getCents() const { return m_cents; }
};
以下示例顯示如何重載operator +(+)以便將兩個“Cents”對象一起添加:
#include <iostream>
class Cents
{
private:
int m_cents;
public:
Cents(int cents) { m_cents = cents; }
// 用友元函數添加Cents + Cents
friend Cents operator+(const Cents &c1, const Cents &c2);
int getCents() const { return m_cents; }
};
// 注意: 這個函數不是成員函數
Cents operator+(const Cents &c1, const Cents &c2)
{
//用Cents構造函數和operator +(int,int)
// 我們可以直接訪問m_cents因爲這是一個友元函數
return Cents(c1.m_cents + c2.m_cents);
}
int main()
{
Cents cents1(6);
Cents cents2(8);
Cents centsSum = cents1 + cents2;
std::cout << "I have " << centsSum.getCents() << " cents." << std::endl;
return 0;
}
這會產生結果:
I have14cents。
重載加號運算符(+)就像聲明一個名爲operator +的函數一樣簡單,爲它提供兩個我們想要添加的操作數類型的參數,選擇一個合適的返回類型,然後編寫該函數。
對於我們的Cents對象,實現我們的operator +()函數非常簡單。首先,參數類型:在這個版本的operator +中,我們將兩個Cents對象一起添加,因此我們的函數將採用兩個Cents類型的對象。第二,返回類型:我們的運算符+將返回類型爲Cents的結果,因此這是我們的返回類型。
最後,實現:要將兩個Cents對象一起添加,我們確實需要從每個Cents對象添加m_cents成員。因爲我們重載的operator +()函數是類的友元函數,所以我們可以直接訪問參數的m_cents成員。安妮日記讀後感(http://m.simayi.net/duhougan/1188.html)此外,因爲m_cents是一個整數,並且C ++知道如何使用與整數操作數一起使用的plus運算符的內置版本將整數添加到一起,所以我們可以簡單地使用+運算符來進行添加。
重載減法運算符( - )也很簡單:
#include <iostream>
class Cents
{
private:
int m_cents;
public:
Cents(int cents) { m_cents = cents; }
// 用友元函數添加Cents + Cents
friend Cents operator+(const Cents &c1, const Cents &c2);
// 用友元函數實現減法 Cents - Cents
friend Cents operator-(const Cents &c1, const Cents &c2);
int getCents() const { return m_cents; }
};
// 注意: 這個函數不是成員函數
Cents operator+(const Cents &c1, const Cents &c2)
{
//用Cents構造函數和operator +(int,int)
// 我們可以直接訪問m_cents因爲這是一個友元函數
return Cents(c1.m_cents + c2.m_cents);
}
// 注意: 這個函數不是成員函數
Cents operator-(const Cents &c1, const Cents &c2)
{
//用Cents構造函數和operator-(int,int)
// 我們可以直接訪問m_cents因爲這是一個友元函數
return Cents(c1.m_cents - c2.m_cents);
}
int main()
{
Cents cents1(6);
Cents cents2(2);
Cents centsSum = cents1 - cents2;
std::cout << "I have " << centsSum.getCents() << " cents." << std::endl;
return 0;
}
重載乘法運算符(*)和除法運算符(/)就像定義operator *和operator /的函數一樣簡單。
友元函數可以在類中定義
即使友元函數不是類的成員,如果需要,它們仍然可以在類中定義:
#include <iostream>
class Cents
{
private:
int m_cents;
public:
Cents(int cents) { m_cents = cents; }
// 用友元函數添加Cents + Cents
// 即使定義在類中,該函數也不被視爲類的成員
friend Cents operator+(const Cents &c1, const Cents &c2)
{
//用Cents構造函數和operator +(int,int)
// 我們可以直接訪問m_cents因爲這是一個友元函數
return Cents(c1.m_cents + c2.m_cents);
}
int getCents() const { return m_cents; }
};
int main()
{
Cents cents1(6);
Cents cents2(8);
Cents centsSum = cents1 + cents2;
std::cout << "I have " << centsSum.getCents() << " cents." << std::endl;
return 0;
}
我們通常不建議這樣做,因爲非一般的函數定義最好保存在類定義之外的單獨的.cpp文件中。但是,我們將在以後的教程中使用此模式來保持示例簡潔。
爲不同類型的操作數重載運算符
通常情況下,您希望重載的運算符使用不同類型的操作數。例如,如果我們有Cents(4),我們可能想要將整數6加到此處以產生結果Cents(10)。
當C ++計算表達式時x + y,x成爲第一個參數,y成爲第二個參數。當x和y具有相同的類型時,如果添加x + y或y + x無關緊要 - 無論哪種方式,都會調用相同版本的operator +。但是,當操作數具有不同的類型時,x + y不會調用與y + x相同的函數。
例如,Cents(4) + 6將調用operator +(Cents,int),並6 + Cents(4)調用operator +(int,Cents)。因此,每當我們爲不同類型的操作數重載二元運算符時,我們實際上需要編寫兩個函數 - 每種情況一個。這是一個例子:
#include <iostream>
class Cents
{
private:
int m_cents;
public:
Cents(int cents) { m_cents = cents; }
// 用友元函數添加Cents + int
friend Cents operator+(const Cents &c1, int value);
// 用友元函數添加 int + Cents
friend Cents operator+(int value, const Cents &c1);
int getCents() { return m_cents; }
};
//注意: 這個函數不是成員函數
Cents operator+(const Cents &c1, int value)
{
// 用友元函數添加Cents + Cents
// 即使定義在類中,該函數也不被視爲類的成員
return Cents(c1.m_cents + value);
}
// 注意: 這個函數不是成員函數
Cents operator+(int value, const Cents &c1)
{
// 用友元函數添加Cents + Cents
// 即使定義在類中,該函數也不被視爲類的成員
return Cents(c1.m_cents + value);
}
int main()
{
Cents c1 = Cents(4) + 6;
Cents c2 = 6 + Cents(4);
std::cout << "I have " << c1.getCents() << " cents." << std::endl;
std::cout << "I have " << c2.getCents() << " cents." << std::endl;
return 0;
}
請注意,兩個重載函數都具有相同的實現 - 這是因爲它們執行相同的操作,它們只是以不同的順序獲取它們的參數。
另一個例子
我們來看看另一個例子:
class MinMax
{
private:
int m_min; // 存放最小值
int m_max; // 存放最大值
public:
MinMax(int min, int max)
{
m_min = min;
m_max = max;
}
int getMin() { return m_min; }
int getMax() { return m_max; }
friend MinMax operator+(const MinMax &m1, const MinMax &m2);
friend MinMax operator+(const MinMax &m, int value);
friend MinMax operator+(int value, const MinMax &m);
};
MinMax operator+(const MinMax &m1, const MinMax &m2)
{
// 在m1和m2中獲取最小值
int min = m1.m_min < m2.m_min ? m1.m_min : m2.m_min;
// 在m1和m2中獲取最大值
int max = m1.m_max > m2.m_max ? m1.m_max : m2.m_max;
return MinMax(min, max);
}
MinMax operator+(const MinMax &m, int value)
{
// 在m1和value中獲取最小值 
int min = m.m_min < value ? m.m_min : value;
// 在m1和value中獲取最大值 
int max = m.m_max > value ? m.m_max : value;
return MinMax(min, max);
}
MinMax operator+(int value, const MinMax &m)
{
// 調用operator+(MinMax, int)
return m + value;
}
int main()
{
MinMax m1(10, 15);
MinMax m2(8, 11);
MinMax m3(3, 12);
MinMax mFinal = m1 + m2 + 5 + 8 + m3 + 16;
std::cout << "Result: (" << mFinal.getMin() << ", " <<
mFinal.getMax() << ")\n";
return 0;
}
MinMax類跟蹤它到目前爲止所見的最小值和最大值。我們已經重載了+運算符3次,因此我們可以將兩個MinMax對象一起添加,或者將整數添加到MinMax對象。
這個例子產生了結果:
Result:(3,16)
您將注意到我們添加到mFinal的最小值和最大值。
讓我們再談談“MinMax mFinal = m1 + m2 + 5 + 8 + m3 + 16”的調用方式。請記住,operator +的優先級高於operator =,operator +從左到右進行求值,因此m1 + m2首先求值。這成爲對運算符+(m1,m2)的調用,它產生返回值MinMax(8,15)。然後MinMax(8,15)+ 5接下來調用。這成爲對operator +(MinMax(8,15),5)的調用,它產生返回值MinMax(5,15)。然後MinMax(5,15)+ 8以相同的方式調用以產生MinMax(5,15)。然後MinMax(5,15)+ m3調用產生MinMax(3,15)。最後,MinMax(3,15)+ 16調用爲MinMax(3,16)。然後將最終結果分配給mFinal。
換句話說,此表達式的計算結果爲“MinMax mFinal =((((((m1 + m2)+ 5)+ 8)+ m3)+ 16)”,對於之後的對象,每次連續操作返回一個成爲左操作數的MinMax對象。
使用其他運算符實現運算符
在上面的例子中,請注意我們通過調用operator +(MinMax,int)來定義operator +(int,MinMax)(它產生相同的結果)。這允許我們將operator +(MinMax,int)的實現減少到一行,通過最小化冗餘並使函數更易於理解,使我們的代碼更易於維護。
通常可以通過調用其他重載運算符來定義重載運算符。如果這樣做會產生更簡單的代碼,您應該這樣做。在實現很簡單的情況下(例如,單行),通常不值得這樣做,因爲附加函數調用比直接實現函數更復雜。
Quiz Time:
1a)編寫一個名爲Fraction的類,它具有整數分子和分母成員。編寫print()函數打印出分數。
以下代碼應該編譯:
#include <iostream>
int main()
{
Fraction f1(1, 4);
f1.print();
Fraction f2(1, 2);
f2.print();
}
這應該打印:
1/4
1/2
解決方案:
#include <iostream>
class Fraction
{
private:
int m_numerator = 0;
int m_denominator = 1;
public:
Fraction(int numerator=0, int denominator=1):
m_numerator(numerator), m_denominator(denominator)
{
}
void print()
{
std::cout << m_numerator << "/" << m_denominator << "\n";
}
};
int main()
{
Fraction f1(1, 4);
f1.print();
Fraction f2(1, 2);
f2.print();
return 0;
}
1b)添加重載乘法運算符以處理分數和整數之間以及兩個分數之間的乘法。使用友元函數方法。
提示:要將兩個分數相乘,首先將兩個分子相乘,然後將兩個分母相乘。要將分數和整數相乘,請將分數的分子乘以整數,並使分母單獨使用。
以下代碼應該編譯:
#include <iostream>
int main()
{
Fraction f1(2, 5);
f1.print();
Fraction f2(3, 8);
f2.print();
Fraction f3 = f1 * f2;
f3.print();
Fraction f4 = f1 * 2;
f4.print();
Fraction f5 = 2 * f2;
f5.print();
Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4);
f6.print();
}
這應該打印:
2/5
3/8
6/40
4/5
6/8
6/24
解決方案
1c)額外信用:分數2/4與1/2相同,但2/4不減少到最低項。我們可以通過找到分子和分母之間的最大公約數(GCD),然後將分子和分母除以GCD,將任何給定分數減少到最低項。
以下是查找GCD的功能:
int gcd(int a, int b)
{
return (b == 0) ? (a > 0 ? a : -a) : gcd(b, a % b);
}
將此函數添加到您的類中,並編寫一個名爲reduce()的成員函數來減少您的分數。確保所有餾分都適當減少。
以下應編譯:
#include <iostream>
int main()
{
Fraction f1(2, 5);
f1.print();
Fraction f2(3, 8);
f2.print();
Fraction f3 = f1 * f2;
f3.print();
Fraction f4 = f1 * 2;
f4.print();
Fraction f5 = 2 * f2;
f5.print();
Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4);
f6.print();
return 0;
}
併產生結果:
2/5
3/8
3/20
4/5
3/4
1/4
解決方案
#include <iostream>
class Fraction
{
private:
int m_numerator;
int m_denominator;
public:
Fraction(int numerator=0, int denominator=1):
m_numerator(numerator), m_denominator(denominator)
{
// 我們在構造函數中放置了reduce()以確保我們不會得到假分數
// 由於所有重載運算符都會創建新的Fractions,因此我們可以保證在此處調用它
reduce();
}
// 我們將使gcd靜態,以便它可以成爲類Fraction的一部分,而不需要使用類型爲Fraction的對象
static int gcd(int a, int b)
{
return (b == 0) ? (a > 0 ? a : -a) : gcd(b, a % b);
}
void reduce()
{
int gcd = Fraction::gcd(m_numerator, m_denominator);
m_numerator /= gcd;
m_denominator /= gcd;
}
friend Fraction operator*(const Fraction &f1, const Fraction &f2);
friend Fraction operator*(const Fraction &f1, int value);
friend Fraction operator*(int value, const Fraction &f1);
void print()
{
std::cout << m_numerator << "/" << m_denominator << "\n";
}
};
Fraction operator*(const Fraction &f1, const Fraction &f2)
{
return Fraction(f1.m_numerator * f2.m_numerator, f1.m_denominator * f2.m_denominator);
}
Fraction operator*(const Fraction &f1, int value)
{
return Fraction(f1.m_numerator * value, f1.m_denominator);
}
Fraction operator*(int value, const Fraction &f1)
{
return Fraction(f1.m_numerator * value, f1.m_denominator);
}
int main()
{
Fraction f1(2, 5);
f1.print();
Fraction f2(3, 8);
f2.print();
Fraction f3 = f1 * f2;
f3.print();
Fraction f4 = f1 * 2;
f4.print();
Fraction f5 = 2 * f2;
f5.print();
Fraction f6 = Fraction(1, 2) * Fraction(2, 3) * Fraction(3, 4);
f6.print();
return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章