多态
基本概念
类别 : 静态多态:函数重载和运算符重载数据静态多态,
动态多态:派生类和虚函数实现运行时多态
区别: 静态多态的函数地址早绑定——编译阶段确定函数地址
动态多态的函数地址晚绑定——运行阶段确定函数地址
class Animal {
public:
void speak() {
cout << "animal is speaking" << endl;
}
virtual void talk() {//虚函数
cout << "animal is talking" << endl;
}
};
class Cat :public Animal {
public:
void speak() {//重写:函数返回值类型 函数名 函数列表 完全相同
cout << "cat is speaking" << endl;
}
void talk() {
cout << "cat is talking" << endl;
}
};
//动态多态:1.需要继承关系 2.派生类重写基类中的虚函数
//动态多态使用:1.基类的指针或者引用 指向派生类对象
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
void doSpeak(Animal& a) {//Animal& a = c;
a.speak();
}
void doTalk(Animal& a) {
a.talk();
}
void test01() {
Cat c;
doSpeak(c);
doTalk(c);
}
当派生类重写基类的虚函数 派生类中的虚函数表 内部 会替换成 派生类的虚函数地址
当基类的指针或引用指向派生类对象时,就会发生多态
Animal类
无重写Cat类
有重写Cat类
多态案例
多态好处:组织结构清晰,可读性强,对于前期和后期扩展以及维护性高
class Calculator {
public:
int getResult(string oper) {
if (oper == "+") {
return m_Num1 + m_Num2;
}else if (oper == "-") {
return m_Num1 - m_Num2;
}else if (oper == "*") {
return m_Num1 * m_Num2;
}
}
int m_Num1;
int m_Num2;
};
void test01() {
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
//如果想扩展新的功能,需要修改源码
//在真实开发中 提倡 开闭原则
//开闭原则:对扩展进行开放,对修改进行关闭
}
class AbstractCalculator {//利用多态实现计算器
public:
virtual int getResult() {
return 0;
}
int m_Num1;
int m_Num2;
};
class AddCalculator :public AbstractCalculator {//加法计算器类
public:
int getResult() {
return m_Num1 + m_Num2;
}
};
class SubCalculator :public AbstractCalculator {//减法计算器类
public:
int getResult() {
return m_Num1 - m_Num2;
}
};
class MulCalculator :public AbstractCalculator {//乘法计算器类
public:
int getResult() {
return m_Num1 * m_Num2;
}
};
void test02() {//多态使用条件:基类指针或引用指向派生类对象
AbstractCalculator* abc = new AddCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
//用完后记得销毁
delete abc;
abc = new SubCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
abc = new MulCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
纯虚函数和抽象类
在多态中,通常父类中虚函数是无用的,主要是调用子类重写的内容,因此可以将虚函数改为纯虚函数,当类中有了纯虚函数,这个类也被称为抽象类。
抽象类特点:1.无法实例化对象;2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类
class Base {
public:
virtual void func() = 0;//纯虚函数:只要有一个纯虚函数,这个类称为抽象类
//抽象类特点:1.无法实例化对象,2.抽象类的派生类必须要重写基类中的纯虚函数,否则也属于抽象类
};
class Son :public Base {
public:
virtual void func() {
cout << "func函数调用" << endl;
}
};
void test01() {
//Base b;//抽象类无法实例化
//new Base;//抽象类无法实例化
Son s; //抽象类的派生类必须要重写基类中的纯虚函数,否则也属于抽象类
Base* base = new Son;
base->func();
}
多态案例
class AbstractDrinking {
public:
virtual void Boil() = 0;//煮水
virtual void Brew() = 0;//冲泡
virtual void PourInCup() = 0;//倒入杯中
virtual void PutSomething() = 0;//加入辅料
void makeDrink() {
Boil();
Brew();
PourInCup();
PutSomething();
}
};
class Coffee :public AbstractDrinking {
public:
virtual void Boil() {
cout << "Boil Water" << endl;
}
virtual void Brew() {
cout << "Brew Coffee" << endl;
}
virtual void PourInCup() {
cout << "Pour In Cup" << endl;
}
virtual void PutSomething() {
cout << "Put Suger" << endl;
}
};
void doWork(AbstractDrinking* abs) {//AbstractDrink* abs = new Coffee;
abs->makeDrink();
delete abs;
}
void test01() {
AbstractDrinking* abs = new Coffee;
doWork(abs);
}
虚析构和纯虚析构
多态使用时,如果将派生类中的属性开辟到堆区,那么基类指针在释放时无法调用到派生类的析构代码
为了解决这一问题,需要将基类中的析构函数改为虚析构或纯虚析构
同时,如果为纯虚析构,则该类属于抽象类,无法实例化对象
class Animal {
public:
Animal() {
cout << "Animal 构造函数调用" << endl;
}
//virtual ~Animal() {//利用虚析构可以解决 基类指针释放派生类对象时不干净的问题
// cout << "Animal 析构函数调用" << endl;
//}
virtual ~Animal() = 0;//纯虚析构:需要有声明,也需要有实现
//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
virtual void speak() = 0;
};
Animal::~Animal() {
cout << "Aniaml 纯虚析构函数调用" << endl;
}
class Cat :public Animal {
public:
Cat(string name) {
cout << "Cat 构造函数调用" << endl;
m_Name = new string(name);
}
virtual void speak() {
cout << *m_Name << " mi mi mi~" << endl;
}
string* m_Name;
~Cat() {
if (m_Name != NULL) {
cout << "Cat 析构函数调用" << endl;
delete m_Name;
m_Name = NULL;
}
}
};
void test01() {
Animal* a = new Cat("Tom");
a->speak();//基类指针在析构时不会调用派生类中析构函数,导致派生类如果有堆区内存,会出现内存泄漏
delete a;
}
案例
class Cpu {
public:
virtual void calculate() = 0;
};
class VideoCard {
public:
virtual void display() = 0;
};
class Memory {
public:
virtual void storage() = 0;
};
class IntelCpu :public Cpu {
void calculate() {
cout << "Inter's Cpu is calculating!!!" << endl;
}
};
class IntelVideoCard :public VideoCard {
void display() {
cout << "Inter's VideoCard is displaying!!!" << endl;
}
};
class IntelMemory :public Memory {
void storage() {
cout << "Inter's Memory is storaging!!!" << endl;
}
};
class HuaweiCpu :public Cpu {
void calculate() {
cout << "Huawei's Cpu is calculating!!!" << endl;
}
};
class HuaweiVideoCard :public VideoCard {
void display() {
cout << "Huawei's VideoCard is displaying!!!" << endl;
}
};
class HuaweiMemory :public Memory {
void storage() {
cout << "Huawei's Memory is storaging!!!" << endl;
}
};
class Computer {
public:
Computer(Cpu* c, VideoCard* vc, Memory* m) {
m_C = c;
m_Vc = vc;
m_M = m;
}
void work() {//工作函数
m_C->calculate();
m_Vc->display();
m_M->storage();
}
~Computer() {
if (m_C != NULL) {
delete m_C;
m_C = NULL;
}
if (m_Vc != NULL) {
delete m_Vc;
m_Vc = NULL;
}
if (m_M != NULL) {
delete m_M;
m_M = NULL;
}
}
private:
Cpu* m_C;
VideoCard* m_Vc;
Memory* m_M;
};
void test01() {
Cpu* intelcpu = new IntelCpu;
VideoCard* intelvideocard = new IntelVideoCard;
Memory* intelmemory = new IntelMemory;
Computer* computer1 = new Computer(intelcpu, intelvideocard, intelmemory);
computer1->work();
delete computer1;
Computer* computer2 = new Computer(new HuaweiCpu, new HuaweiVideoCard, new HuaweiMemory);
computer2->work();
delete computer2;
}
文件
C++中对文件操作需要包含头文件<fstream>
文件类型分为两种:1.文本文件:文件以文本的ASCII码形式存储在计算机中
2.二进制文件:文件以文本的二进制形式存储在计算机中
操作文件的三大类:1.ofstream:写操作
2.ifstream:读操作
3.fstream:读写操作
文本文件
文件打开方式可以配合使用,利用
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
#include<fstream>//头文件包含
void test01() {
ofstream ofs;//创建流对象
ofs.open("text.txt", ios::out);//指定打开方式 ofs.open("文件路径",打开方式)
ofs << "name:Tom" << endl;//写文件
ofs << "sex:man" << endl;
ofs << "age:18" << endl;
ofs.close();//关闭文件
}
void test02() {
ifstream ifs;//创建流对象
ifs.open("text.txt", ios::in);//打开文件
if (!ifs.is_open()) {//判断是否打开
cout << "file open fail" << endl;
return;
}
//读数据 第一种
/*char buf[1024] = { 0 };//循环到结束,自动退出
while (ifs >> buf) {
cout << buf << endl;
}*/
//第二种
/*char buf[1024] = { 0 };
while (ifs.getline(buf, sizeof(buf))) {
cout << buf << endl;
}*/
//第三种
/*string buf;
while (getline(ifs, buf)) {
cout << buf << endl;
}*/
//第四种 EOF:end of file 文件尾
char c;
while ((c = ifs.get()) != EOF) {
cout << c;
}
ifs.close();//关闭文件
}
二进制文件
打开方式要指定为ios::binary
二进制方式写文件主要利用流对象调用成员函数write
#include<fstream>
class Person {
public:
char m_Name[64];//姓名
int m_Age;//年龄
};
void test01() {
//ofstream ofs;//创建流对象
//ofs.open("Person.txt", ios::out | ios::binary);//两个打开方式同时进行
ofstream ofs("Person.txt", ios::out | ios::binary);//通过构造函数直接创造
Person p = { "tom",18 };
ofs.write((const char*)&p, sizeof(Person));//写文件
ofs.close();//关闭文件
}
void test02() {
ifstream ifs;
ifs.open("Person.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "file open fail" << endl;
}
Person p;
ifs.read((char*)&p, sizeof(Person));//读文件
cout << "name: " << p.m_Name << " age: " << p.m_Age << endl;
ifs.close();//关闭文件
}