多态是C++面向对象三大特性之一
多态分为两类
静态多态和动态多态区别:
动态多态满足条件
动态多态使用条件
#include
using namespace std;
// 动物类
class Animal
{
public:
// 虚函数 virtual
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
// 猫类
class Cat :public Animal
{
public:
// 重写:函数返回值类型 函数名 参数列表 三个完全相同
void speak()
{
cout << "小猫在说话" << endl;
}
};
//狗类
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
// 我们希望传入什么对象,那么就调用什么对象的函数
// 如果函数地址在编译阶段就能确定,那么静态联编
// 如果函数地址在运行阶段才能确定,就是动态联编
// 执行说话的函数
// 地址早绑定,在编译阶段就确定函数地址
// 如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
void dospeak(Animal& animal)// 类似Animal &animal=cat,父类引用指向子类的传递对象
{
animal.speak();// 加了virtual后,由于加入的对象不同,确定不同函数地址
}
void test01()
{
Cat cat;
dospeak(cat);
Dog dog;
dospeak(dog);
}
void test02()
{
cout << "sizeof Animal = " << sizeof(Animal) << endl;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
// 普通写法
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;//操作数1
int m_Num2;//操作数2
};
void test01()
{
//创建计算器对象
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}
多态好处:
总结:C++开发提倡利用多态设计程序架构,因为多态优点很多
#include
#include
using namespace std;
// 分别利用普通写法和多态技术实现计算器
// 普通写法
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;// 操作数1
int m_Num2;// 操作数2
};
void test01()
{
// 创建计算器对象
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}
// 多态好处:
// 1.组织结构清晰
// 2.可读性强
// 3.对于前期和后期扩展以及维护性高
// 利用多态实现计算器
// 实现计算器的抽象类
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()
{
// 多态使用条件
// 父类指针或者引用指向子类对象
// new数据设置在堆区(用父类的指针)
// 加法运算
// 创建一个加法计算器的对象,用父类的指针指向子类的对象
AbstractCalculator* abc = new AddCalculator;// abc 为指针
abc->m_Num1 = 100;
abc->m_Num2 = 100;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
//用完后手动释放堆区数据,数据释放了,但abc还是父类的指针
delete abc;
//减法运算
abc = new SubCalculator;
abc->m_Num1 = 100;
abc->m_Num2 = 100;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
//乘法运算
abc = new MulCalculator;
abc->m_Num1 = 100;
abc->m_Num2 = 100;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
delete abc;
}
int main()
{
test01();
cout << endl;
test02();
system("pause");
return 0;
}
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数。
virtual 返回值类型 函数名 (参数列表)= 0 ;
抽象类特点:
#include
using namespace std;
// 多态的意义是让子类重写父类中的虚函数
// 纯虚函数 和 抽象类
class Base
{
public:
// 只要有一个纯虚函数,这个类称为抽象类
// 抽象类特点:
// 1.无法实例化对象
// 2.抽象类的子类 必须重写父类中的纯虚函数,否则也属于抽象类,无法实例化对象
virtual void func() = 0;// 纯虚函数 语法
};
class Son :public Base // 继承
{
public:
virtual void func() // 重写父类中的纯虚函数
{
cout << "func 函数调用" << endl;
};
};
void test01()
{
// Base b;// 抽象类是无法实例化对象
// new Base;// 抽象类是无法实例化对象
Son s;// 子类必须重写父类中的纯虚函数(子类加virtual),否则无法实例化对象
Base* base = new Son;// new 什么对象,就调用什么对象 func 函数
base->func(); // 调用子类 func 函数
}
int main()
{
test01();
system("pause");
return 0;
}
案例描述:
#include
using namespace std;
// 多态案例2 制作饮品
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 << "煮农夫山泉" << endl;
}
// 冲泡
virtual void Brew()
{
cout << "冲泡咖啡" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
// 加入辅料
virtual void PutSomething()
{
cout << "加入糖和牛奶" << endl;
}
};
// 制作茶叶
class Tea :public AbstractDrinking
{
public:
// 煮水
virtual void Boil()
{
cout << "煮农夫山泉" << endl;
}
// 冲泡
virtual void Brew()
{
cout << "冲泡茶叶" << endl;
}
// 倒入杯中
virtual void PourInCup()
{
cout << "倒入杯中" << endl;
}
// 加入辅料
virtual void PutSomething()
{
cout << "加入枸杞" << endl;
}
};
// 制作函数
void doWork(AbstractDrinking* abs)// 父类的指针
{
abs->makeDrink();
// 制作完delete
delete abs;// 堆区数据手动开辟,手动释放
}
void test01()
{
// 制作咖啡
doWork(new Coffee);// AbstractDrinking *abs = new Coffee
cout << " --------------- " << endl;
// 制作茶叶
doWork(new Tea);
}
int main()
{
test01();
system("pause");
return 0;
}
虚析构和纯虚析构共性:
虚析构和纯虚析构区别:
虚析构语法:virtual ~类名(){}
纯虚析构语法:类内声明 —— virtual ~类名() = 0;
类外实现 —— 类名::~类名(){}
总结:
#include
#include
using namespace std;
// 虚析构和纯虚析构 —— 解决子类中的代码调用不到的问题,存在指针类型,在堆区
class Animal
{
public:
Animal() // 无参构造函数调用
{
cout << "Animal 构造函数调用" << endl;
}
// 利用虚析构可以解决 父类指针释放子类对象时不干净的问题
virtual ~Animal() // 析构函数,虚析构,这样Cat析构函数就会调用出来
{
cout << "Animal 虚析构函数调用" << endl;
}
// 纯虚析构, 与虚析构只能有一个存在, 需要声明也需要实现
// 有了纯虚析构之后,这个类也属于抽象类,无法实例化对象
// virtual ~Animal() = 0;
// 纯虚函数,与纯虚析构不同,不需要实现
virtual void speak() = 0;
};
// Animal 下的纯虚析构
//Animal::~Animal()
//{
// cout << "Animal 纯虚析构函数调用" << endl;
//}
class Cat :public Animal
{
public:
Cat(string name)// 有参构造函数
{
cout << "Cat 构造函数调用" << endl;
m_Name = new string(name);// new 一个string,返回的是string指针
}
virtual void speak() // 子类重写父类
{
cout << *m_Name << " 小猫在说话" << endl;
}
~Cat() // 析构函数
{
if (m_Name != NULL)
{
cout << "Cat 析构函数调用" << endl;
delete m_Name;
m_Name == NULL;
}
}
public:
string *m_Name;// 让小猫的名称创建在堆区
};
void test01()
{
Animal* animal = new Cat("Tom");
animal->speak();
// 父类指针在析构时,不会调用子类中析构函数,导致子类如果有堆区的属性,出现内存的泄露情况
delete animal;
}
int main()
{
test01();
system("pause");
return 0;
}
// 纯虚析构, 与虚析构只能有一个存在, 需要声明也需要实现
// virtual ~Animal() = 0;
// Animal 下的纯虚析构
//Animal::~Animal()
//{
// cout << "Animal 纯虚析构函数调用" << endl;
//}
// 纯虚函数,与纯虚析构不同,不需要实现
virtual void speak() = 0;
案例描述:
// 抽象出每个零件的类
// 1.抽象CPU类
class CPU // 抽象类
{
public:
// 抽象计算函数
virtual void calculate() = 0;
};
// 2.抽象显卡类
class VideoCard // 抽象类
{
public:
// 抽象显示函数
virtual void display() = 0;
};
// 3.抽象内存条类
class Memory // 抽象类
{
public:
// 抽象存储函数
virtual void storage() = 0;
};
// 电脑类
class Computer
{
// 构造函数中传入三个零件指针
public:
// 有参构造
Computer(CPU* cpu, VideoCard* vc, Memory* mem)
{
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
// 提供工作函数
void work()
{
// 让零件工作起来,调用接口
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
// 提供析构函数 释放3个电脑零件
~Computer()
{
// 释放CPU零件
if (m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
// 释放显卡零件
if (m_vc != NULL)
{
delete m_vc;
m_vc = NULL;
}
// 释放内存条零件
if (m_mem != NULL)
{
delete m_mem;
m_mem = NULL;
}
}
private:
CPU* m_cpu;// CPU的零件指针
VideoCard* m_vc;// 显卡的零件指针
Memory* m_mem;// 内存条零件指针
};
// Inter厂商
class InterCpu :public CPU
{
public:
virtual void calculate()
{
cout << "Inter CPU开始计算" << endl;
}
};
class InterVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "Inter 显卡开始显示" << endl;
}
};
class InterMemory :public Memory
{
public:
virtual void storage()
{
cout << "Inter 内存条开始存储" << endl;
}
};
// 联想厂商
class LenovoCpu :public CPU
{
public:
virtual void calculate()
{
cout << "Lenovo CPU开始计算" << endl;
}
};
class LenovoVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "Lenovo 显卡开始显示" << endl;
}
};
class LenovoMemory :public Memory
{
public:
virtual void storage()
{
cout << "Lenovo 内存条开始存储" << endl;
}
};
#include
using namespace std;
// 抽象出每个零件的类
// 1.抽象CPU类
class CPU // 抽象类
{
public:
// 抽象计算函数
virtual void calculate() = 0;
};
// 2.抽象显卡类
class VideoCard // 抽象类
{
public:
// 抽象显示函数
virtual void display() = 0;
};
// 3.抽象内存条类
class Memory // 抽象类
{
public:
// 抽象存储函数
virtual void storage() = 0;
};
// 电脑类
class Computer
{
// 构造函数中传入三个零件指针
public:
Computer(CPU* cpu, VideoCard* vc, Memory* mem)
{
m_cpu = cpu;
m_vc = vc;
m_mem = mem;
}
// 提供工作函数
void work()
{
// 让零件工作起来,调用接口
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
// 提供析构函数 释放3个电脑零件
~Computer()
{
// 释放CPU零件
if (m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
// 释放显卡零件
if (m_vc != NULL)
{
delete m_vc;
m_vc = NULL;
}
// 释放内存条零件
if (m_mem != NULL)
{
delete m_mem;
m_mem = NULL;
}
}
private:
// 电脑的类中有三个指针维护三个零件
CPU* m_cpu;// CPU的零件指针
VideoCard* m_vc;// 显卡的零件指针
Memory* m_mem;// 内存条零件指针
};
// 具体零件厂商
// Inter厂商
class InterCpu :public CPU
{
public:
virtual void calculate()
{
cout << "Inter CPU开始计算" << endl;
}
};
class InterVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "Inter 显卡开始显示" << endl;
}
};
class InterMemory :public Memory
{
public:
virtual void storage()
{
cout << "Inter 内存条开始存储" << endl;
}
};
// 联想厂商
class LenovoCpu :public CPU
{
public:
virtual void calculate()
{
cout << "Lenovo CPU开始计算" << endl;
}
};
class LenovoVideoCard :public VideoCard
{
public:
virtual void display()
{
cout << "Lenovo 显卡开始显示" << endl;
}
};
class LenovoMemory :public Memory
{
public:
virtual void storage()
{
cout << "Lenovo 内存条开始存储" << endl;
}
};
// 组装不同的电脑
void test01()
{
//第一台电脑组装,CPU为父类,InterCpu是子类
CPU* intelCpu = new InterCpu;// 多态技术,用父类的指针指向子类对象
VideoCard* intelCard = new InterVideoCard;
Memory* intelMem = new InterMemory;
cout << "第一台电脑开始工作:" << endl;
//创建第一台电脑
Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
computer1->work();
delete computer1;
cout << "-----------------------" << endl;
cout << "第二台电脑开始工作:" << endl;
//第二台电脑组装
Computer* computer2 = new Computer(new LenovoCpu, new LenovoVideoCard, new LenovoMemory);;
computer2->work();
delete computer2;
cout << "-----------------------" << endl;
cout << "第三台电脑开始工作:" << endl;
//第三台电脑组装
Computer* computer3 = new Computer(new LenovoCpu, new InterVideoCard, new LenovoMemory);;
computer3->work();
delete computer3;
}
int main()
{
test01();
system("pause");
return 0;
}
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放。通过文件可以将数据持久化,C++中对文件操作需要包含头文件 < fstream >
文件类型分为两种:
操作文件的三大类:
写文件步骤如下:
包含头文件:#include
创建流对象 :ofstream ofs
打开文件:ofs.open(“文件路径”,打开方式)
写数据:ofs << “写入的数据”
关闭文件:ofs.close()
文件打开方式:
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意: 文件打开方式可以配合使用,利用|操作符
**例如:**用二进制方式写文件 ios::binary | ios:: out
#include
#include // 头文件包含
using namespace std;
// 文本文件 写文件
void test01()
{
// 1.包含头文件 fstream
// 2.创建流对象
ofstream ofs;
// 3.指定打开的方式 文件名,写文件
// 不指定路径的话,txt在文档文件下,右击打开文件夹
ofs.open("test.txt", ios::out);
// 4.写内容
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
// 5.关闭文件
ofs.close();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
读文件步骤如下:
包含头文件:#include
创建流对象:ifstream ifs;
打开文件并判断文件是否打开成功:ifs.open(“文件路径”,打开方式);
读数据:四种方式读取
关闭文件:ifs.close();
#include
#include
#include
using namespace std;
// 文本文件 读文件
void test01()
{
// 1.包含头文件
// 2.创建流对象
ifstream ifs;
// 3.打开文件并且判断是否打开成功
ifs.open("test.txt", ios::in);
if (!ifs.is_open())// 反过来判断 open 失败
{
cout << "文件打开失败" << endl;
return;
}
// 4.读数据
//第一种
char buf[1024] = { 0 };
while (ifs >> buf)
{
cout << buf << endl;
}
// 第二种
char buf[1024] = { 0 };// buf指向数组的首地址
while (ifs.getline(buf, sizeof(buf)))
{
cout << buf << endl;
}
//第三种
/*string buf;
while (getline(ifs, buf))
{
cout << buf << endl;
}*/
//第四种
char c;
while (c = ifs.get() != EOF)//EOF表示end of file 文件尾部
{
cout << c;
}
//5.关闭文件
ifs.close();
}
int main()
{
test01();
system("pause");
return 0;
}
总结:
ostream& write(const char * buffer,int len);
总结:
#include
#include
#include
using namespace std;
class Person
{
public:
char m_Name[64];// 尽量不用c++的string
int m_Age;
};
// 二进制文件 写文件
void test01()
{
// 1、包含头文件
// 2、创建输出流对象
ofstream ofs("person.txt", ios::out | ios::binary);
// 3、打开文件
//ofs.open("person.txt", ios::out | ios::binary);
Person p = { "张三" , 18 };
// 4、写文件
ofs.write((const char*)&p, sizeof(p));
// 5、关闭文件
ofs.close();
}
int main()
{
test01();
system("pause");
return 0;
}
istream& read(char *buffer,int len);
#include
#include
#include
using namespace std;
class Person
{
public:
char m_Name[64];// 姓名
int m_Age;// 年龄
};
// 二进制文件 读文件
void test01()
{
// 1.包含头文件
// 2.创建流对象
ifstream ifs;
// 3.打开文件 判断文件是否打开成功
ifs.open("person.txt", ios::in | ios::binary);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
// 4.读文件
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << "姓名:" << p.m_Name << "年龄" << p.m_Age << endl;
// 5.关闭文件
ifs.close();
}
int main()
{
test01();
system("pause");
return 0;
}