20、C++多态—(纯虚函数,抽象类,虚析构,纯虚析构)

本文详细介绍了C++中的多态特性,包括静态多态和动态多态,以及如何通过虚函数实现运行时多态。通过实例展示了动态多态在类继承中的作用,强调了虚函数在子类父类同名函数调用中的关键作用。此外,还探讨了纯虚函数和抽象类的概念,指出它们在接口设计和对象实例化中的限制。最后,通过多个实际案例,如计算器、饮品制作和电脑组装,阐述了多态在实际编程中的应用和重要性。
摘要由CSDN通过智能技术生成

一、多态的基本语法

多态:静态多态、动态多态

静态多态函数重载运算符重载

动态多态派生类和虚函数实现运行时多态

区别:

静态多态函数地址早就绑定 - 编译阶段确定函数地址

动态多态函数地址晚绑定 - 运行阶段确定函数地址

————————————————

class Animal
{

public:
    void speak()
    {
        cout << "动物在说话。" << endl;
    }
};

class Cat :public Animal
{

public:
    void speak()
    {
        cout << "小猫在说话。" << endl;
    }
};

void doSpeak(Animal& animal)  //父类引用接收子类对象
{                              //Animal& animal = cat;
    animal.speak();
}

   Cat cat;
   doSpeak(cat);   // ”动物在说话。“ //子父类函数同名,执行的父类的函数

—————————————————

class Animal
{

public:
    void virtual speak()   //虚函数
    {
        cout << "动物在说话。" << endl;
    }
};

class Cat :public Animal
{

public:
    void speak()
    {
        cout << "小猫在说话。" << endl;
    }
};

void doSpeak(Animal& animal)  //父类引用接收子类对象
{                                                      //Animal& animal = cat;
    animal.speak();        //这个函数地址还不能确定下来,多种形态,

                                        传入对象不同,不同函数确定不同地址
}

   Cat cat;
   doSpeak(cat);   // ”小猫在说话。“   //子父类函数同名,执行的子类的函数(因为virtual)

__________________________

当子类、父类成员函数同名时, 子类对象调用同名函数成员时,

父类的该同名函数前加virtual,则其函数地址在编译阶段才确定,

所以最终调用了子类的同名成员函数

——————————————————————

总结:子类父类存在同名成员函数,通过父类引用指针调用子类同名函数时,将父类该同名函数声明为virtual,则此时,调用的就是子类对象

(1)、动态多态满足条件:

        1、有继承关系

        2、子类重写父类虚函数

(2)、动态多态的使用:

        父类的指针或者引用,执行子类对象

——————————————————————

#include<iostream>
using namespace std;

class Animal
{
public:
	void virtual 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();  //这个函数地址还不能确定下来,多种形态,传入对象不同,不同函数确定不同地址
}

void test01()
{
	Cat cat;
	doSpeak(cat);

	Dog dog;
	doSpeak(dog);
}


int main()
{
	test01();

	system("pause");
	return 0;
}

一、多态的基本语法02(多态原理剖析)

二、多态案例:计算器类

普通写法多态技术,实现两个操作数进行运算的计算器类 

1、普通实现

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

//开闭原则:对扩展进行开发,对修改进行关闭
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;
		}
	}
public:
	int m_Num1;
	int m_Num2;
};

void test01()
{
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 5;

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

int main()
{
	test01();

	system("pause");
	return 0;
}

2、多态实现

多态优点:

(1)、代码组织结构清晰

(2)、可读性强

(3)、利于前期和后期的扩展、维护

——————————————————

abs = new SubCalculator;
abs->m_A = 10;
abs->m_B = 5;
cout << abs->m_A << " - " << abs->m_B << " = " << abs->getResult() << endl;
delete abs;   //abs指向堆区开辟内存,用完之后堆区内存销毁,abs 任然是父类指针

——————————————————

每次都要重新赋值,虽然用的是父类指针,但继承下来的成员具体值,确实自己开辟赋值

#inlcude<iostream>
using namespace std;

class AbstractCalculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
public:
	int m_A;
	int m_B;
};

class AddCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_A + m_B;
	}
};

class SubCalculator : public AbstractCalculator
{
public:
	int getResult()
	{
		return m_A - m_B;
	}
};

class MulCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_A * m_B;
	}
};

void test02()
{
	AbstractCalculator* abs = new AddCalculator;
	abs->m_A = 10;
	abs->m_B = 5;
	cout << abs->m_A << " + " << abs->m_B << " = " << abs->getResult() << endl;
	delete abs;   //new delete 成套使用

	abs = new SubCalculator;
	abs->m_A = 10;
	abs->m_B = 5;
	cout << abs->m_A << " - " << abs->m_B << " = " << abs->getResult() << endl;
	delete abs;   //abs指向堆区开辟内存,用完之后堆区内存销毁,abs 任然是父类指针

	abs = new MulCalculator;
	abs->m_A = 10;
	abs->m_B = 5;
	cout << abs->m_A << " * " << abs->m_B << " = " << abs->getResult() << endl;
	delete abs;
}


int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

.

三、纯虚函数和抽象类

多态中父类虚函数一般毫无意义,主要是通过调用子类重写的内容。

我们可以将虚函数声明为纯虚函数

纯虚函数特点:

(1)、无法实例化对象

(2)、子类必须重写纯虚函数,否则子类也为抽象类

含有纯虚函数的类,称为抽象类

#include<iostream>
using namespace std;

class Base
{
public:
	//纯虚函数
	virtual void func() = 0;
};

class Son :public Base
{
	void func() 
	{
		cout << "Son func()" << endl;
	}
};


void test01()
{
	//纯虚函数无法实例化对象
	//Base b;
	//new Base;

	//子类必须重写纯虚函数,否则子类也为抽象类
	//Son s;   //未重写
	Son s;

	Base* b = new Son;
	b->func();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

.

四、多态案例2:制作饮品

制作饮品的流程:煮水 - 冲泡 - 倒杯 - 加佐料    (抽象类

制作Coffee: 煮水 - 冲泡 - 倒杯 - 加佐料          (继承抽象类,实例化

制作Tea: 煮水 - 冲泡 - 倒杯 - 加佐料                (继承抽象类,实例化

一个抽象基类接口,实现多态(通过基类指针调用派生类,重新定义的纯虚函数)

#include<iostream>
using namespace std;

//制作饮品抽象类
class AbstractDrinking
{
public:
	//煮水
	virtual void Boil() = 0;

	//冲泡
	virtual void Brew() = 0;

	//倒杯
	virtual void PoutInCup() = 0;

	//加佐料
	virtual void PutSomething() = 0;

	void makeDrink()
	{
		Boil();
		Brew();
		PoutInCup();
		PutSomething();
	}
};

//制作咖啡
class Coffee :public AbstractDrinking
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "1.煮沸农夫山泉" << endl;
	}

	//冲泡
	virtual void Brew()
	{
		cout << "2.冲泡咖啡" << endl;
	}

	//倒杯
	virtual void PoutInCup()
	{
		cout << "3.将咖啡倒入杯中" << endl;
	}

	//加佐料
	virtual void PutSomething()
	{
		cout << "4.加入糖和牛奶" << endl;
	}
};

//制作茶水
class Tea :public AbstractDrinking
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "1.煮沸矿泉水" << endl;
	}

	//冲泡
	virtual void Brew()
	{
		cout << "2.冲泡茶叶" << endl;
	}

	//倒杯
	virtual void PoutInCup()
	{
		cout << "3.将茶水倒入杯中" << endl;
	}

	//加佐料
	virtual void PutSomething()
	{
		cout << "4.加入枸杞" << endl;
	}
};

void doWork(AbstractDrinking* abs)
{
	abs->makeDrink();
	delete abs;
}


void test01()
{
	doWork(new Coffee);

	cout << endl;

	doWork(new Tea);

}

int main()
{
	test01();

	system("pause");
	return 0;
}

.

五、虚析构和纯虚析构

在继承中,利用多态时,如果存在子类对象在堆区开辟内存的情况,则通过父类指针调用子类对象时,没有办法释放子类堆区空间。

解决办法:使用虚析构

1、虚析构和纯虚析构共性:

        都可以解决父类指针,释放子类对象

        都需要有具体的函数实现代码

2、虚析构和纯虚析构区别:

        纯虚析构,该类属于抽象类,无法实例化对象

——————————————

Animal* animal = new Cat("花花");
animal->speak();
delete animal;        //调用父类析构函数时,不会调用子类析构函数

                                //导致子类堆区内容无法释放

——————————————————

父类Animal的析构函数声明为virtual之后,

调用父类析构时,也会调用子类析构

—————————————————— 

 (1)、纯虚析构,有声明,也需要实现(防止有堆区,不释放)

(2)、如果子类没有在堆区开辟内存的情况,可以不写虚析构和纯虚析构。

(3)、拥有纯虚函数的类,也属于抽象类 

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

class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	//virtual ~Animal()
	//{
	//	cout << "Animal~虚析构函数调用" << endl;
	//}
	
	//纯虚析构,有声明,也需要实现(防止有堆区,不释放)
	//有了纯虚析构以后,这个类也属于抽象类,无法实例化
	virtual ~Animal() = 0;
public:
	virtual void speak() = 0;
};

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


class Cat :public Animal
{
public:
	Cat(string name)
	{
		m_Name = new string(name);
		cout << "Cat构造完成" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			delete m_Name;
			m_Name = NULL;
			cout << "Cat~析构完成" << endl;
		}
	}
public:
	void speak()
	{
		cout << *m_Name << "在说话" << endl;
	}
public:
	string* m_Name;
};

void test01()
{
	Animal* animal = new Cat("花花");
	animal->speak();
	//调用父类析构函数时,不会调用子类析构函数
	//导致子类堆区内容无法释放
	delete animal;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

六、多态案例3:电脑组装

电脑零件:CPU、显卡、内存条

生产零件厂商:Lenovo、Inter

要求:三种各种厂商零件,通过组装得到一台电脑

————————————————

CPU抽象类:Lenovo的CPU、Intel的CPU

显卡抽象类:Lenovo的显卡、Intel的显卡

内存条抽象类:Lenovo的内存条、Intel的内存条

--------------------------

电脑类(传入抽象类指向的派生类)

{

        进行组装;

}

————————————————

#include<iostream>
using namespace std;

//抽象类
class CPU
{
public:
	virtual void calculator() = 0;
};

class VideCard
{
public:
	virtual void display() = 0;
};

class Memory
{
public:
	virtual void storage() = 0;
};


//电脑类
class Computer
{
public:
	Computer(CPU* cpu, VideCard* vc, Memory* m)
	{
		m_cpu = cpu;
		m_vc = vc;
		m_m = m;
		//cout << "Computer 构造了" << endl;
	}
	~Computer()
	{
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
			//cout << "cpu 析构了" << endl;
		}
		if (m_vc != NULL)
		{
			delete m_vc;
			m_vc = NULL;
			//cout << "videCard 析构了" << endl;
		}
		if (m_m != NULL)
		{
			delete m_m;
			m_m = NULL;
			//cout << "memory 析构了" << endl;
		}
	}
public:
	//组装
	void make()
	{
		m_cpu->calculator();
		m_vc->display();
		m_m->storage();
	}
private:
	CPU* m_cpu;
	VideCard* m_vc;
	Memory* m_m;
};


//Inter 产商零件
class IntelCpu :public CPU
{
public:
	//IntelCpu()
	//{
	//	cout << "Intel cpu 构造了" << endl;
	//}
public:
	void calculator()
	{
		cout << "Intel CPU" << endl;
	}
};

class IntelVideCard :public VideCard
{
public:
	//IntelVideCard()
	//{
	//	cout << "Intel VideCard 构造了" << endl;
	//}
	void display()
	{
		cout << "Intel VideCard" << endl;
	}
};

class IntelMemory :public Memory
{
public:
	//IntelMemory()
	//{
	//	cout << "Intel Memory 构造了" << endl;
	//}
	void storage()
	{
		cout << "Intel Memory" << endl;
	}
};


//Lenovo 产商零件
class LenovoCpu :public CPU
{
public:
	void calculator()
	{
		cout << "Lenovo CPU" << endl;
	}
};

class LenovoVideCard :public VideCard
{
public:
	void display()
	{
		cout << "Lenovo VideCard" << endl;
	}
};

class LenovoMemory :public Memory
{
public:
	void storage()
	{
		cout << "Lenovo Memory" << endl;
	}
};

void test01()
{
	Computer *computer1 = new Computer(new IntelCpu, new IntelVideCard, new IntelMemory);
	computer1->make();
	delete computer1;   //delete computer1时,会调用析构函数

	cout << "------------------------" << endl;
	Computer* computer2 = new Computer(new LenovoCpu, new LenovoVideCard, new LenovoMemory);
	computer2->make();
	delete computer2;

	cout << "------------------------" << endl;
	Computer* computer3 = new Computer(new LenovoCpu, new IntelVideCard, new LenovoMemory);
	computer3->make();
	delete computer3;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值