视频27——重载<<操作符
/** \brief
*
* 运算符"<<"重载实现有理数(分式)的加减乘除
*
*/
#include<iostream>
using namespace std;
#include<string>
#include<cmath>
//定义基类
class Rational
{
public:
//构造器
Rational(int num1, int den1);
//析构器
~Rational();
//方法
Rational operator + (Rational rhs);//rhs = right hand side(右边的参数)
Rational operator - (Rational ths);
Rational operator * (Rational ths);
Rational operator / (Rational ths);
private:
void normalize();//负责对分数的简化处理
int num;//分子
int den;//分母
//友元函数
friend ostream &operator << (ostream &os, Rational f);//对运算符<<重载
};
//构造函数实现
Rational::Rational(int num1, int den1)
{
num = num1;
den = den1;
normalize();//对分数化为最简形式
}
//析构器的实现呢
Rational::~Rational()
{
}
//基类的方法实现
void Rational::normalize()
{
//normalize()对分数进行简化操作包括:
//1.只允许分子为负数,如果分母为负数则把负数挪到分子部分,如1/-2==-1/2
//2.利用欧几里德算法(辗转求余原理)将分数进行简化:2/10 => 1/5
if(den < 0)
{
den = -den;
num = -num;
}
//辗转求余原理:求解分子和分母的最大公约数
int a = abs(num);
int b = abs(den);
while(b > 0)
{
int t = a % b;
a = b;
b = t;
}
//分子,分母同时除以最大公约数
num = num / a;
den = den / a;
}
//分式加法运算
//a c a*d c*b a*d + c*d
//- + - = --- + --- = ----------
//b d b*d b*d b*d
Rational Rational::operator + (Rational rhs)
{
int e,f;
e = num * rhs.den + rhs.num * den;
f = den * rhs.den;
return Rational(e,f);
}
//分式减法运算【转化为加法运算】
//a c a -c
//- - - = - + --
//b d b d
Rational Rational::operator - (Rational rhs)
{
rhs.num = -rhs.num;
return operator + (rhs);
}
//分式乘法运算
//a c a*c
//- * - = ---
//b d b*d
Rational Rational::operator*(Rational rhs)
{
int e,f;
e = num * rhs.num;
f = den * rhs.den;
return Rational(e,f);
}
//分式除法运算
//a c a d
//- / - = - * -
//b d b c
Rational Rational::operator/(Rational rhs)
{
int e,f;
e = num * rhs.den;
f = den * rhs.num;
return Rational(e,f);
}
//函数声明
//并不属于Rational类,是一个独立的函数
ostream &operator << (ostream &os, Rational f);
int main()
{
Rational c1(3,4);
Rational c2(2,4);
//有理数加法验证
//第一、三、五个<<检测到后面c1是Rational类型,执行重载函数
//第二、四、六个<<检测到后面的“+”不是Rational类型,执行打印输出功能
cout << c1 << " + " << c2 << " == " << (c1+c2) << "\n";
//有理数减法验证
cout << "\n\n";
cout << c1 << " - " << c2 << " == " << (c1-c2) << "\n";
//有理数乘法验证
cout << "\n\n";
cout << c1 << " * " << c2 << " == " << (c1*c2) << "\n";
//有理数除法验证
cout << "\n\n";
cout << c1 << " / " << c2 << " == " << (c1/c2) << "\n";
return 0;
}
//并不属于Rational类,是一个独立的函数
ostream &operator << (ostream &os, Rational f)
{
if(f.num % f.den == 0)//此时分子是分母的倍数,打印整数
os << f.num / f.den;
else
os << f.num << "/" << f.den;//打印分数
return os;
}
PS:一般来说,在调用operator<<()重载函数时,传递给它的是哪一个流,它返回的就应该是那个流的一个引用。所以程序中声明写成ostream &operator << (ostream &os, Rational f);
视频28——多继承
#include <iostream>
using namespace std;
#include <string>
基类:人
class Person
{
public:
//构造器
Person(string theName);
//基类的方法
void introduce();
protected:
string name;
};
//基类构造器实现
Person::Person(string theName)
{
name = theName;
}
//基类方法实现
void Person::introduce()
{
cout << "我是一位" << name << endl;
}
子类1:老师
class Teacher : public Person
{
public:
//子类构造器
Teacher(string theName, string theClass);//老师的名字,在哪个班级教学
//子类的方法
void teach();//教书
//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
void introduce();//自我介绍
protected:
string classes;
};
//子类构造器实现
Teacher::Teacher(string theName, string theClass) : Person(theName)//老师的名字继承于人类中的名字
{
classes = theClass;
}
//子类方法实现
void Teacher::teach()
{
cout << "我教" << classes << endl;
}
void Teacher::introduce()//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
{
cout << "大家好,我是" << name << ",我教" << classes << endl;
}
子类2:学生
class Student : public Person
{
public:
//构造器
Student(string theName, string theClass);//学生姓名,上什么课程
//方法
void attent();//上课
void introduce();//自我介绍
protected:
string classes;
};
//子类2构造器实现
Student::Student(string theName, string theClass):Person(theName)
{
//因为name已经继承基类,所以此时无需再幅值。
classes = theClass;
}
//子类方法实现
void Student::attent()
{
cout << name << "加入" << classes << "学习" << endl;
}
void Student::introduce()
{
cout << "我是" << name << ",我在" << classes << "学习" << endl;
}
子类3:助教
//多继承,既继承老师,也继承学生
class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
//构造器
Assistant(string theName, string classTeach, string classAttend);
//方法
void introduce();
};
//助教:构造器实现
Assistant::Assistant(string theName, string classTeach, string classAttend):
Teacher(theName,classTeach),Student(theName,classAttend)
{
;
}
void Assistant::introduce()
{
//这里注意Student::name不是Student.name
cout << "大家好,我是" << Student::name << "上课在" << Student::classes;
cout << "同时教学在" << Teacher::classes << "\n";
}
int main()
{
Teacher teacher("小甲鱼", "C++入门");
Student student("迷途羔羊", "C++入门");
Assistant assistent("丁丁", "C++入门", "C++进阶");
//老师
teacher.introduce();
teacher.teach();
//学生
cout << "\n";
student.introduce();
student.attent();
//助教
cout << "\n";
assistent.introduce();
assistent.teach();
assistent.attent();
return 0;
}
PS:在程序的101和102行中“Student::name不是Student.name”,因为此时Student类未实例化为类的对象,所以只能用Student::name而不是Student.name。
视频29——虚继承
/**< 虚继承可以让Student和Teacher类都虚继承自Person类,
编译器将确保从Student和Teacher类再派生出来的子类
只能拥有一份Person类的属性
*/
#include <iostream>
using namespace std;
#include <string>
基类:人
class Person
{
public:
//构造器
Person(string theName);//theName为类的输入参数
//基类的方法
void introduce();
protected:
string name;
};
//基类构造器实现
Person::Person(string theName)
{
name = theName;
}
//基类方法实现
void Person::introduce()
{
cout << "我是一位" << name << endl;
}
子类1:老师
//虚继承,再有类继承Teacher类时,将只拥有一份Person类的属性
class Teacher : virtual public Person
{
public:
//子类构造器
Teacher(string theName, string theClass);//老师的名字,在哪个班级教学
//子类的方法
void teach();//教书
//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
void introduce();//自我介绍
protected:
string classes;
};
//子类构造器实现
Teacher::Teacher(string theName, string theClass) : Person(theName)//老师的名字继承于人类中的名字
{
classes = theClass;
}
//子类方法实现
void Teacher::teach()
{
cout << "我教" << classes << endl;
}
void Teacher::introduce()//该函数不是重载,不是覆盖,而是子类隐藏了基类方法
{
cout << "大家好,我是" << name << ",我教" << classes << endl;
}
子类2:学生
//虚继承,再有类继承Student类时,将只拥有一份Person类的属性
class Student : virtual public Person
{
public:
//构造器
Student(string theName, string theClass);//学生姓名,上什么课程
//方法
void attent();//上课
void introduce();//自我介绍
protected:
string classes;
};
//子类2构造器实现
Student::Student(string theName, string theClass):Person(theName)
{
//因为name已经继承基类,所以此时无需再幅值。
classes = theClass;
}
//子类方法实现
void Student::attent()
{
cout << name << "加入" << classes << "学习" << endl;
}
void Student::introduce()
{
cout << "我是" << name << ",我在" << classes << "学习" << endl;
}
子类3:助教
//多继承,既继承老师,也继承学生
class Assistant:public Teacher,public Student//!!注意这里都需要写public
{
public:
//构造器
Assistant(string theName, string classTeach, string classAttend);
//方法
void introduce();
};
//助教:构造器实现
//由于虚继承,Teacher和Student都不能拥有Person类中的属性和方法
//只能由Person类自己给出
Assistant::Assistant(string theName, string classTeach, string classAttend):
Teacher(theName,classTeach),
Student(theName,classAttend),
Person(theName)
{
//多继承 助教既继承老师类,又继承学生类
}
void Assistant::introduce()
{
//这里注意Student::name不是Student.name
cout << "大家好,我是" << Student::name << "上课在" << Student::classes;
cout << "同时教学在" << Teacher::classes << "\n";
}
int main()
{
Teacher teacher("小甲鱼", "C++入门");
Student student("迷途羔羊", "C++入门");
Assistant assistent("丁丁", "C++入门", "C++进阶");
//老师
teacher.introduce();
teacher.teach();
//学生
cout << "\n";
student.introduce();
student.attent();
//助教
cout << "\n";
assistent.introduce();
assistent.teach();
assistent.attent();
return 0;
}
视频30——错误处理和调试
程序出错可以分为两大类:编译时出错(complie-time error)和运行时错误(run-time error)
1.编译时出错
suggest1:培养并保持一种编程风格
suggest2:认真对待编译器给出的错误/警告信息
suggest3:写代码三思而后行(先画流程图)
suggest4:注意检查最基本的语法
suggest5:把可能有问题的代码行注释
suggest6:换一个环境或开发工具试试
suggest7:检查自己是否已经把所有必要的头文件全部include进来
suggest8:留意变量的作用域和命名空间
suggest9:休息一下
suggest10:使用调试工具
最后避免错误的另一个好方法就是把调试好的代码另外保存起来并不再改动它,然后把代码划分成各个模块,用它们来搭建新的应用程序。
2.运行时出错
suggest1:培养保持良好编程风格
suggest2:多用注释,用好注释
suggest3:注意操作符的优先级
suggest4:不要忘记对用户输入和文件输入进行合理性检查
suggest5:不要做任何假设,想当然
suggest6:把程序划分成一些比较小的单元模块来测试
视频31——错误处理和调试2
#include <iostream>
using namespace std;
#include <climits>//C语言中的头文件
class Factorial
{
public:
Factorial(unsigned short num);
unsigned long getFactorial();
bool inRange();
private:
unsigned short num;
};
Factorial::Factorial(unsigned short num)
{
this->num = num;//传入的参数num值给了Factorial类中的属性num
}
unsigned long Factorial::getFactorial()
{
unsigned long sum = 1;
for(int i = 1; i <= num; i++)
{
sum *= i;
}
return sum;
}
bool Factorial::inRange()
{
unsigned long max = ULONG_MAX;//ULONG_MAX存在于climits
//将最大值ULONG_MAX除以输入的num阶乘
for(int i = num; i >= 1; --i)
{
max /= i;
}
if(max < 1)
return false;
else
return true;
}
int main()
{
unsigned short num = 0;
cout << "请输入一个整数:";
cin >> num;
Factorial fac(num);
if(fac.inRange())
{
cout << num << "的阶乘值为" << fac.getFactorial() << endl;
}
else
{
cout << "你所输入的值太大了!!!" << endl;
}
return 0;
}
视频32——assert函数和捕获异常
#include <cassert>
//assert()函数需要有一个参数,它将测试这个输入参数的真或者假状态
//如果为真 Do nothing
//如果为假 Do something
int main()
{
int i = 5;
//如果括号内为真,则过;假则中断程序
assert(i == 10);
return 0;
}
捕获异常
为了对付潜在的编程错误(尤其是在运行时的错误),捕获异常是一种完全不同的方法。简单说,异常就是与预期不相符合的反常现象。
#include <iostream>
using namespace std;
#include <climits>
unsigned long returnFactorial(unsigned short num) throw(const char*)
{
unsigned long sum = 1;
unsigned long max = ULONG_MAX;
for(int i = 1; i <= num; i++)
{
sum *= i;
max /= i;
}
if(max < 1)
{
throw"悲催。。。该基数太大,无法在该计算机计算求出阶乘值。\n";
}
else
{
return sum;
}
}
int main()
{
unsigned short num = 0;
cout << "请输入一个整数:";
while(!(cin >> num) || (num < 1))
{
cin.clear();//清除状态
cin.ignore(100, '\n');//清除缓存区
cout << "请输入一个整数:";
}
cin.ignore(100, '\n');//清除缓存区
try
{
//如果异常,则抛出字符型的*e异常
//直接跳转到catch继续执行
unsigned long factorial = returnFactorial(num);
cout << num << "的阶乘为:" << factorial << endl;
}
catch(const char *e)
{
cout << "error!!";
}
return 0;
}