c++学习心得第二期 多态+文件操作

本文详细探讨了C++中的多态性,包括静态多态与动态多态的概念,以及如何通过虚函数、纯虚函数和抽象类实现多态。同时,通过计算器、饮品制作和电脑组装等案例展示了多态的实际应用。此外,还介绍了文件操作,包括文本文件和二进制文件的读写操作,强调了虚析构函数在多态内存管理中的作用。
摘要由CSDN通过智能技术生成

一、多态

1、多态的基本概念

多态是C++面向对象三大特性之一
多态分为两类
静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址

#include<iostream>
#include<string>
using namespace std;
//因为早绑定,所以地址已经是animal的地址了  想执行小猫说话但结果是动物说话
class Animal{

public:
	void speak()
	{
		cout <<"动物在说话" << endl;
	}
};
class Cat :public Animal{
public:
	void speak()
	{
		cout <<"小猫在说话" << endl;
	}

};
//执行说话函数
//地址早绑定 在编译阶段确定函数的地址。
void doSpeak(Animal &animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);

}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

#include<iostream>
#include<string>
using namespace std;
class Animal{
//在函数前加 virtual 变成动态绑定。
public:
	virtual void   speak()
	{
		cout <<"动物在说话" << endl;
	}
};
class Cat :public Animal{
public:
	void speak()
	{
		cout <<"小猫在说话" << endl;
	}

};
void doSpeak(Animal &animal)
{
	animal.speak();
}
void test01()
{
	Cat cat;
	doSpeak(cat);

}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

多态满足条件
有继承关系
子类重写父类中的虚函数
多态使用条件
父类指针或引用指向子类对象
重写:函数返回值类型 函数名 参数列表 完全一致称为重写
在这里插入图片描述

2、多态案例一-计算器类

分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
代码组织结构清晰
可读性强
利于前期和后期的扩展以及维护

#include<iostream>
#include<string>
using namespace std;

class AbstractCalculator{
	
public :
	virtual int result() = 0;
public:
	int num1;
	int num2;
};
class AddCalculator:public AbstractCalculator{

public:
	AddCalculator(int num1,int num2)
	{
		this->num1 = num1;
		this->num2 = num2;
	}
	int result()
	{
		return num1 + num2;
	}
};
class SubstractorCalculator :public AbstractCalculator{

public:
	SubstractorCalculator(int num1, int num2)
	{
		this->num1 = num1;
		this->num2 = num2;
	}
	int result()
	{
		return num1 - num2;
	}
};
class MultiplyrCalculator :public AbstractCalculator{

public:
	MultiplyrCalculator(int num1, int num2)
	{
		this->num1 = num1;
		this->num2 = num2;
	}
	int result()
	{
		return num1*num2;
	}
};
void test01()
{
	AbstractCalculator *abc = new AddCalculator(10, 10);
	cout << abc->result() << endl;
	delete abc;

	abc = new SubstractorCalculator(20,10);
	cout << abc->result() << endl;
	delete abc;

	abc = new MultiplyrCalculator(20, 10);
	cout << abc->result() << endl;
	delete abc;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

3、纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类

#include<iostream>
#include<string>
using namespace std;
//纯虚函数
//类中只要有一个纯虚函数就称为抽象类
//抽象类无法实例化对象
//子类必须重写父类中的纯虚函数,否则也属于抽象类
class Base{
public:
	virtual void func() = 0;
};
class Son :public Base{
public:
	void func()
	{
		cout <<"调用了 func函数" << endl;
	}

};
void test01()
{
	Base *base;
	//设置了纯虚函数即为抽象函数,因此抽象函数不能实例化
	//base = new Base;
	base = new Son();
	base->func();
	delete base;//分配到堆区的内存要删除
}
int main()
{
	test01();
	system("pause");
	return 0;
}

4、多态案例二-制作饮品

制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include<iostream>
#include<string>
using namespace std;
class AbstractDrinking
{
public:
	virtual void Boil() = 0;
	virtual void Brew() = 0;
	virtual void PourInCup() = 0;
	virtual void PusSomething() = 0;

	void MakeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PusSomething();
	}
};

class Coffee :public AbstractDrinking
{
	void Boil()
	{
		cout <<"矿泉水" << endl;
	}
	void Brew()
	{
		cout <<"泡" << endl;
	}
	void PourInCup()
	{
		cout <<"倒" << endl;
	}
	void PusSomething()
	{
		cout <<"加牛奶" << endl;
	}


};
class Tea :public AbstractDrinking
{
	void Boil()
	{
		cout << "自来水" << endl;
	}
	void Brew()
	{
		cout << "加热" << endl;
	}
	void PourInCup()
	{
		cout << "泼" << endl;
	}
	void PusSomething()
	{
		cout << "加树叶" << endl;
	}
};
//使用引用调用虚函数和多态
void DoWork(AbstractDrinking& drinking)
{
	drinking.MakeDrink();
}
//使用指针调用虚函数和多态
void DoWorkByPoint(AbstractDrinking *dringing)
{
	dringing->MakeDrink();
}
void test01()
{
	//使用引用调用虚函数和多态
	Tea tea;
	DoWork(tea);
	Coffee coffee;
	DoWork(coffee);
	cout <<"------------------>" << endl;
	//使用指针调用虚函数和多态
	AbstractDrinking *drinking = new Tea;
	DoWorkByPoint(drinking);
	delete drinking;
	drinking = new Coffee;
	DoWorkByPoint(drinking);
	delete drinking;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

5、虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}

总结:

  1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
  2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
  3. 拥有纯虚析构函数的类也属于抽象类
#include<iostream>
#include<string>
using namespace std;

class Animal{
public:
	Animal()
	{
		cout <<"Animal 构造函数的调用" << endl;
	}
	~Animal()
	{
		cout <<"Animal 析构函数的调用" << endl;
	}
	virtual void speak() = 0;

};

class Cat :public Animal
{
public:
	Cat()
	{
		cout <<"小猫的构造函数在调用" << endl;
	}
	~Cat()
	{
		cout <<"小猫的析构函数在调用" << endl;
	}
	void speak()
	{
		cout <<"小猫在叫" << endl;
	}
};

void test01()
{
	Animal* animal = new Cat;
	animal->speak();
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

小猫的析构函数没有调用,如果小猫中存在堆区的分配,有开辟的内存空间没有释放会造成内存泄漏。因此要在父类中使用虚析构和纯虚析构,可以是子类中的析构函数被调用。

#include<iostream>
#include<string>
using namespace std;

class Animal{
public:
	Animal()
	{
		cout <<"Animal 构造函数的调用" << endl;
	}
	virtual ~Animal() = 0;
	
	virtual void speak() = 0;

};
Animal:: ~Animal()
{
	cout << "Animal 析构函数的调用" << endl;
}

class Cat :public Animal
{
public:
	Cat(string name)
	{
		cout <<"小猫的构造函数在调用" << endl;
		m_Name = new string(name);
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			delete m_Name;
			m_Name = NULL;
		}
		cout <<"小猫的析构函数在调用" << endl;
	}
	void speak()
	{
		cout <<*m_Name<<"小猫在叫" << endl;
	}
	string *m_Name;
};

void test01()
{
	Animal* animal = new Cat("Jack");
	animal->speak();
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

6、多态案例三-电脑组装

电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作

#include<iostream>
#include<string>
using namespace std;

//创建 cpu memory videocard 虚函数  创建Inter和Lenovo对虚函数进行实现。 
//创建 computer 作为cpu memory 和 videocard的集合类  进行组装
class Cpu{
public:
	virtual void calculate() = 0;
};
class VideoCard
{
public:
	virtual void display() = 0;
};
class Memory
{
public:
	virtual void storage() = 0;
};
class Computer
{
public:
	Computer(Cpu* c,Memory *m,VideoCard *v)
	{
		cpu = c;
		memory = m;
		videocard = v;
	}
	~Computer()
	{
		if (cpu != NULL)
		{
			delete cpu;
			cpu = NULL;
		}
		if (memory != NULL)
		{
			delete cpu;
			memory = NULL;
		}
		if (videocard != NULL)
		{
			delete cpu;
			videocard = NULL;
		}
	}
	void work()
	{
		cpu->calculate();
		memory->storage();
		videocard->display();
	}
private:
	Cpu *cpu;
	Memory *memory;
	VideoCard *videocard;
};

//具体厂商
//Intel厂商
class IntelCPU :public Cpu
{
public:
	virtual void calculate()
	{
		cout << "Intel的CPU开始计算了!" << endl;
	}
};

class IntelVideoCard :public VideoCard
{
public:
	virtual void display()
	{
		cout << "Intel的显卡开始显示了!" << endl;
	}
};

class IntelMemory :public Memory
{
public:
	virtual void storage()
	{
		cout << "Intel的内存条开始存储了!" << endl;
	}
};

//Lenovo厂商
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 * intelCpu = new IntelCPU;
	VideoCard * intelCard = new IntelVideoCard;
	Memory * intelMem = new IntelMemory;

	cout << "第一台电脑开始工作:" << endl;
	//创建第一台电脑
	Computer * computer1 = new Computer(intelCpu, intelMem, intelCard);
	computer1->work();
	delete computer1;

	cout << "-----------------------" << endl;
	cout << "第二台电脑开始工作:" << endl;
	//第二台电脑组装
	Computer * computer2 = new Computer(new LenovoCPU, new LenovoMemory,new LenovoVideoCard);
	computer2->work();
	delete computer2;

	cout << "-----------------------" << endl;
	cout << "第三台电脑开始工作:" << endl;
	//第三台电脑组装
	Computer * computer3 = new Computer(new LenovoCPU, new LenovoMemory, new LenovoVideoCard);
	computer3->work();
	delete computer3;

}
int main()
{
	test01();
	system("pause");
	return 0;
}

二、文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件 < fstream >
文件类型分为两种:
文本文件 - 文件以文本的ASCII码形式存储在计算机中
二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
ofstream:写操作
ifstream: 读操作
fstream : 读写操作

1.文本写文件

写文件步骤如下:
包含头文件
#include
创建流对象
ofstream ofs;
打开文件
ofs.open(“文件路径”,打开方式);
写数据
ofs << “写入的数据”;
关闭文件
ofs.close();

在这里插入图片描述

注意: 文件打开方式可以配合使用,利用|操作符
**例如:**用二进制方式写文件 ios::binary | ios:: out

#include<iostream>
using namespace std;
#include<fstream>

void test01()
{
	ofstream ofs;
	ofs.open("text.txt",ios::out);
	ofs << "姓名:张三" << endl;
	ofs << "性别:男" << endl;
	ofs.close();
}

int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述在这里插入图片描述

文件操作必须包含头文件 fstream
读文件可以利用 ofstream ,或者fstream类
打开文件时候需要指定操作文件的路径,以及打开方式
利用<<可以向文件中写数据
操作完毕,要关闭文件

2.文本读文件

读文件与写文件步骤相似,但是读取方式相对于比较多
读文件步骤如下:
包含头文件
#include
创建流对象
ifstream ifs;
打开文件并判断文件是否打开成功
ifs.open(“文件路径”,打开方式);
读数据
四种方式读取
关闭文件
ifs.close();

#include<iostream>
using namespace std;
#include<fstream>
#include<string>

void test01()
{
	ifstream ifs;
	ifs.open("text.txt",ios::in);
	if (!ifs.is_open())
	{
		cout <<"文件打开失败" << endl;
		return;
	}

	string buf;
	while (getline(ifs,buf))
	{
		cout <<buf << endl;

	}

}

int main()
{
	test01();
	system("pause");
	return 0;
}

3、二进制文件

以二进制的方式对文件进行读写操作
打开方式要指定为 ios::binary

4、二进制写文件

二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

文件输出流对象 可以通过write函数,以二进制方式写数据

#include<iostream>
using namespace std;
#include<fstream>
#include<string>

class Person{
public:
	char m_Name[64];
	int m_Age;
};
//二进制写文件
void test01()
{
	//创建输出流对象
	ofstream ofs("person.txt",ios::out|ios::binary);
	Person p = {"张三",18};
	ofs.write((const char*)&p,sizeof(p));
	ofs.close();
}

int main()
{
	test01();
	system("pause");
	return 0;
}

5、二进制读文件

二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

文件输入流对象 可以通过read函数,以二进制方式读数据

#include<iostream>
using namespace std;
#include<fstream>
#include<string>

class Person{
public:
	char m_Name[64];
	int m_Age;
};
//二进制写文件
void test01()
{
	ifstream ifs("person.txt",ios::in|ios::binary);
	if (!ifs.is_open())
	{
		cout <<"文件打开失败" << endl;
	}
	Person p;
	ifs.read((char*)&p,sizeof(p));
	cout <<"姓名  " <<p.m_Name <<"  年龄  " <<p.m_Age<< endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值