多态的实质:父类的引用或指针指向子类对象
怎么理解? 假设我们现在有如下的类,其中Animal为基类,Cat和Dog都是其派生类。
class Animal{
public:
virtual void speak(){
cout << "动物在说话" << endl;
}
};
class Cat :public Animal{
public:
void speak(){
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal{
public:
void speak(){
cout << "小狗在说话" << endl;
}
};
1.父类指针指向子类对象 调用时,可以定义父类的指针,然后指向子类的对象,指向哪个对象,便执行其对象的虚函数实现
int main() {
方法1:
Dog B;
Animal *A=&B;
A->speak();
如果你不想给Dog类取名:
Animal *A=new Dog;
A->speak();
方法2:
A=new Cat;
A->speak();
return 0;
}
2.父类引用指向子类对象 这种实现,我们需要额外定义一个“实现函数”,其参数是基类的引用(否则不能实现多态)。这样在调用时就实现了父类引用指向子类对象。
void DoSpeak(Animal & X){
X.speak();
}
int main() {
Cat A;
DoSpeak(A);
Dog B;
DoSpeak(B);
Animal C;
DoSpeak(C);
return 0;
}
. . .
一.静态联编与动态联编 多态分为:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
private:
};
class Cat:public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
private:
};
void DoSpeak(Animal& animal) 多态的实质:父类的引用或指针指向子类对象
{
animal.speak();
}
void test01()
{
Cat cat;
DoSpeak(cat);
}
int main()
{
test01();
system("pause");
return 0;
}
. . . 二.多态原理解析
-
当父类中有了虚函数(virtual)后,内部结构发生了改变 -
内部多了一个virptf指针
- virtual function pointer 虚函数表指针
- 指向vftable虚函数表
-
父类中的结构有:virptf虚函数表指针 vtfable虚函数表 -
子类中进行继承,会继承父类的virptf虚函数表指针 vtfable虚函数表 -
在子类的构造函数中,会自动将自己继承下来的virptf虚函数表指针指向自己的vtfable虚函数表 -
如果子函数中发生了重写,会替换掉vtfable虚函数表中原有的speak,变成Cat::speak
- 重写:子类写speak父类的虚函数,重写必须返回值,参数个数,类型,顺序都相同
. . . 三.抽象基类和纯虚函数 如果父类中有一些函数,它不需要任何的实现,只想让子类通过多态做新的实现(如计算机案例),此时可以用抽象类,也就是在类中加入至少一个纯虚函数,使得基类被称为抽象类
- 纯虚函数使用关键字virtual,并在其后面加上=0,如果试图去实例化一个抽象类,编译器会制止(报错)
- 当Calculator是一个抽象基类时
Calculator cal ; 和Calculator * cal = new Add_Calculator 都会报错!(也就是Calculator里面含有纯虚函数) - 当继承一个抽象类时,必须实现所有的纯虚函数(也就是通过多态进行改写),否则由抽象类派生的类也是一个抽象类
. . .
四.虚析构和纯虚析构
虚析构:
- 调用形式:virtual~类名(){}
- 解决问题:通过父类指针指向子类对象释放时释放不干净的问题(普通的析构是不会调用子类的析构的,也就是子类的析构函数无法被调用,所以可能会释放不干净)
纯虚析构函数:
- 调用形式:virtual~类名()=0 类内声明,类外实现
类名::~类名(){} - 如果出现了纯虚构函数,这个类也算是抽象类,不能进行实例化
以给小动物取名为例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
~Animal()
{
cout << "Animal的析构函数调用" << endl;
}
private:
};
class Cat :public Animal
{
public:
Cat(const char* name)
{
this->m_name = new char[strlen(name) + 1];
strcpy(this->m_name, name);
}
void speak()
{
cout << this->m_name << "在说话" << endl;
}
virtual ~Cat()
{
cout << "Cat的析构函数调用" << endl;
}
private:
char* m_name;
};
void test01()
{
Animal* animal = new Cat("TOM");
animal->speak();
delete animal;
}
int main()
{
test01();
system("pause");
return 0;
}
如果不调用虚析构函数,那么Cat类的析构不会被调用 调用了虚析构函数,才会把子类的数据给清理掉! 纯析构函数的写法
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
virtual~Animal() = 0;
private:
};
Animal::~Animal()
{
cout << "纯虚析构函数调用" << endl;
}
文章转自布尔博客 欢迎关注技术公众号,获取更多硬件学习干货!
我们能为你提供什么? 技术辅导:C++、Java、嵌入式软件/硬件 项目辅导:软件/硬件项目、大厂实训项目 就业辅导:就业全流程辅导、技术创业支持 对接企业HR:培养输送优质性人才
|