文档章节

C++基础教程面向对象学习笔记及心得感悟[图]

 原创小博客
发布于 10/15 20:03
字数 3173
阅读 11
收藏 0

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

© 著作权归作者所有

共有 人打赏支持
粉丝 5
博文 120
码字总数 144293
作品 0
宜昌
私信 提问
大神有话说之c++,还在迷茫的朋友可以来看一下

C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。C++ 可运行于多种平台上,如 Window...

悟空_b201
05/30
0
0
C++零基础教程之类和对象初识

C++ 类和对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理...

这个人很懒什么都没留下
09/03
0
0
c语言基础学习11_项目实战:IDE(集成开发环境)

============================================================================= ============================================================================= 涉及到的知识点有: 一......

黑泽明军
01/29
0
0
C++程序设计与数据结构算法视频教程

课程目标:使学员深入了解C++语言,能够熟练使用C++编写程序。通过学习数据结构,能用C++编写出更加灵活,巧妙,健壮的代码。学完全部课程后,学员具备一定的编程思想和编程逻辑,从而对其他...

星辰8209
07/26
0
0
Java程序员如何高效而优雅地入门Cpp?

java切入到cpp的学习,首先会具备以下几个优势点 1.没有初学编程语言的恐惧感 毕竟作为过来人切入新的编程语言的学习,驾轻就熟学习的套路上都已经很熟悉了,如果是中级的java程序员切入进来...

启示录是真的
05/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

当程序员有了中年危机 你会发现你就是个屁

前言 程序员是一个怎样的存在?引用一句鸡汤的名言来说:你以为你用双手改变了世界,实际上是苍老了自己。为什么我今天会抛出这个话题,其实我也是一个懵懂的少年,我也曾经为了成为一名程序...

架构师springboot
14分钟前
0
0
大型网站B2C商城项目实战+MongoDB+Redis+zookeeper+MySQL

本文列出了当今计算机软件开发和应用领域最关键部分,如果你想保证你现在以及未来的几年不失业,那么你最好跟上这些技术的发展。虽然你不必对这十种技术样样精通,但至少应该对它们非常熟悉。...

java知识分子
15分钟前
1
0
大型企业网络系统集成方案如何设计?

网络系统集成是企业实现无纸化办公和即时通讯办公的基础建设,在以生产效率为核心竞争力的市场中,企业想要快速获取信息并有效提高企业工作效率及业务能力,企业网络系统集成是必不可少的,由...

Java干货分享
16分钟前
0
0
Spring应用学习——IOC

1. Spring简介 1. Spring的出现是为了取代EJB(Enterprise JavaBean)的臃肿、低效、脱离现实的缺点。Spring致力于J2EE应用的各层(表现层、业务层、持久层)的解决方案,Spring是企业应用开...

江左煤郎
17分钟前
0
0
用Redis轻松实现秒杀系统

导论 曾经被问过好多次怎样实现秒杀系统的问题。昨天又在CSDN架构师微信群被问到了。因此这里把我设想的实现秒杀系统的价格设计分享出来。供大家参考。 秒杀系统的架构设计 秒杀系统,是典型...

James-
24分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部