C++基础教程面向对象学习笔记及心得感悟[图]
使用友元函数重载算术运算符:
C ++中一些最经常使用的运算符是算术运算符 - 即加号运算符(+),减运算符( - ),乘法运算符(*)和除法运算符(/)。请注意,全部算术运算符都是二元运算符 - 这意味着它们须要两个操做数 - 运算符的每一侧都有一个操做数。全部这四个运算符都以彻底相同的方式过载。
事实证实,有三种不一样的方法来重载运算符:成员函数方式,友元函数方式和正常函数方式。在本课中,咱们将介绍友元函数的方式(由于它对大多数二元运算符更直观)。下一课,咱们将讨论正常的函数方式。最后,在本章后面的课程中,咱们将介绍成员函数的方式。固然,咱们还将总结什么时候更详细地使用每个。html
使用友元函数重载运算符:
考虑如下的类:
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;
}ios