文章目录
- 1.多态的介绍
- 1.1概念
- 1.2分类
- 2.多态的实现
- 2.1虚函数的介绍
- 1.定义
- 2.重写
- 1.通常情况
- 2.特例
- 2.2多态构成条件
- 2.3破坏多态的情况
- 1.父类没有虚函数 子类有对应虚函数重写
- 2.父类有虚函数 子类没有重写对应虚函数
- 2.4虚析构和纯虚析构
- 1.虚析构
- 2.纯虚析构
- 3.虚析构/纯虚析构存在的意义
- 2.5总结及拓展
- 1.final:修饰父类虚函数,表示该虚函数不能再被重写
- 2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
- 3.重载、覆盖(重写)、隐藏(重定义)
- 2.6抽象类
- 1.概念
- 2.接口继承和实现继承
- 3.代码演示
1.多态的介绍
1.1概念
简单来说,同样一种行为,不同对象完成时会呈现不同的状态。比如:
- 吃东西 牛吃草 狼吃羊 猫吃鱼
- 买票 学生票 军人票 普通票
1.2分类
静态多态: 函数重载 和 运算符重载 — 编译阶段确定函数地址
动态多态: 虚函数 — 运行阶段确定函数地址
2.多态的实现
2.1虚函数的介绍
1.定义
被virtual修饰的类的成员函数
2.重写
1.通常情况
派生类重写基类的虚函数:派生类中有一个跟基类完全相同的虚函数(返回值类型、函数名字、参数列表完全相同)
2.特例
- 子类重写父类的虚函数【不加virtual也构成重写–继承基类的虚函数后实现】
- 协变:返回值是父子关系的指针或引用–返回值不同也构成重写
对照组:
class Animal
{
public:
virtual void express()
{
cout << "我在疯狂动物叫" << endl;
}
};
class Dog :public Animal
{
public:
virtual void express()
{
cout << "我在疯狂狗叫" << endl;
}
};
void func(Animal& animal)
{
animal.express();
}
int main()
{
Animal animal;
func(animal);
Dog dog;
func(dog);
return 0;
}
实验组1:
class Animal
{
public:
virtual Animal* express()
{
cout << "我在疯狂动物叫" << endl;
return this;
}
};
class Dog :public Animal
{
public:
virtual Dog* express()
{
cout << "我在疯狂狗叫" << endl;
return this;
}
};
void func(Animal& animal)
{
animal.express();
}
int main()
{
Animal animal;
func(animal);
Dog dog;
func(dog);
return 0;
}
实验组2:
class Ox //牛
{
};
class Bull :public Ox//公牛
{
};
class Animal
{
public:
virtual Ox* express()
{
cout << "我在疯狂动物叫" << endl;
return nullptr;
}
};
class Dog :public Animal
{
public:
virtual Bull* express()
{
cout << "我在疯狂狗叫" << endl;
return nullptr;
}
};
void func(Animal& animal)
{
animal.express();
}
int main()
{
Animal animal;
func(animal);
Dog dog;
func(dog);
return 0;
}
2.2多态构成条件
- 指向子类对象的基类指针或引用调用虚函数
- 派生类重写基类的虚函数
class Animal
{
public:
virtual void express()
{
cout << "我在疯狂动物叫" << endl;
}
};
class Dog :public Animal
{
public:
virtual void express()
{
cout << "我在疯狂狗叫" << endl;
}
};
void func(Animal& animal)
{
animal.express();
}
int main()
{
Animal animal;
func(animal);
Dog dog;
func(dog);
return 0;
}
例题
class A
{
public:
virtual void func(int value = 1)
{
cout << "A->" << value << endl;
}
virtual void test()
{
func();
}
};
class B : public A
{
public:
void func(int value = 0)
{
cout << "B->" << value << endl;
}
};
int main()
{
A* pb = new B; //B -> 1
pb->test();
return 0;
}
2.3破坏多态的情况
1.父类没有虚函数 子类有对应虚函数重写
class Dad
{
public:
void Cook()
{
cout << "佛跳墙" << endl;
}
virtual void Work()
{
cout << "Work" << endl;
}
int _a = 0;
};
class Son : public Dad
{
public:
virtual void Cook()
{
cout << "方便面" << endl;
}
int _b = 0;
};
void Test(Dad& p)
{
p.Cook();
}
int main()
{
Dad dad;
Test(dad);
Son son;
Test(son);
return 0;
}
父类没有虚函数表 编译时就已经确定了函数地址 即不论父类或子类对象调用的都是父类的成员函数
2.父类有虚函数 子类没有重写对应虚函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
using namespace std;
class Dad
{
public:
virtual void Cook()
{
cout << "佛跳墙" << endl;
}
virtual void Work()
{
cout << "Work" << endl;
}
int _a = 0;
};
class Son : public Dad
{
public:
/*virtual void Cook()
{
cout << "方便面" << endl;
}*/
int _b = 0;
};
void Test(Dad& p)
{
p.Cook();
}
int main()
{
Dad dad;
Test(dad);
Son son;
Test(son);
return 0;
}
父类有虚函数 运行时才确定函数地址 只不过子类没有重写 虚函数表存放的均为父类的虚函数地址 所以均调用父类的成员函数
2.4虚析构和纯虚析构
1.虚析构
1.一般情况:调用完子类析构后自动调用父类析构
class Person
{
public:
Person()
{
cout << "Person()" << endl;
}
~Person()
{
cout << "~Person()" << endl;
}
//int* _ptr;
};
class Student : public Person
{
public:
Student()
{
cout << "Student()" << endl;
}
// 析构函数名底层为:destructor -- 构成虚函数重写
~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
Student s;
return 0;
}
2.没有虚析构导致错误的场景
class Person
{
public:
Person()
{
cout << "Person()" << endl;
}
~Person()
{
cout << "~Person()" << endl;
}
//int* _ptr;
};
class Student : public Person
{
public:
Student()
{
cout << "Student()" << endl;
}
// 析构函数名底层为:destructor -- 构成虚函数重写
~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
Person* ptr1 = new Person;
delete ptr1;
Person* ptr2 = new Student;
delete ptr2;
return 0;
}
3.成功样例
class Person
{
public:
Person()
{
cout << "Person()" << endl;
}
virtual ~Person()
{
cout << "~Person()" << endl;
}
//int* _ptr;
};
class Student : public Person
{
public:
Student()
{
cout << "Student()" << endl;
}
// 析构函数名底层为:destructor -- 构成虚函数重写
virtual ~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
Person* ptr1 = new Person;
delete ptr1;
Person* ptr2 = new Student;
delete ptr2; //ptr2 -> destructer();
//operator delete(ptr2);
return 0;
}
2.纯虚析构
class Dad
{
public:
Dad()
{
cout << "Dad 构造函数调用!" << endl;
}
//虚析构函数
//virtual ~Dad()
//{
// cout << "Dad 虚析构函数调用!" << endl;
//}
//**虚析构和纯虚析构都必须有函数【实现】
//纯虚析构函数
virtual ~Dad() = 0;
virtual void Name() = 0;
};
Dad::~Dad()
{
cout << "Dad 纯虚析构函数调用!" << endl;
}
class Son : public Dad
{
public:
Son(string name)
{
cout << "Son 构造函数调用!" << endl;
_name = new string(name);
}
~Son()
{
cout << "Son 析构函数调用!" << endl;
if (this->_name != NULL)
{
delete _name;
_name = NULL;
}
}
virtual void Name()
{
cout << *_name << "是son的名字" << endl;
}
public:
string* _name;
};
int main()
{
Dad* dad = new Son("Mike");
dad->Name();
delete dad;
return 0;
}
3.虚析构/纯虚析构存在的意义
- 虚析构语法:
virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0;
类名::~类名(){}
- 二者目的皆是能够【delete指向子类对象的父类指针】时正确调用析构函数。
区别在于:纯虚析构适用于:当前基类作为一个抽象类,不想要实例化对象,只作为子类的父类,并且可以强制子类重写析构函数。
但是使用虚析构和纯虚析构需要注意:二者必须有函数实现–虚析构在类内即可完成函数实现–纯虚析构需要在类外完成。
如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
2.5总结及拓展
1.final:修饰父类虚函数,表示该虚函数不能再被重写
2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
3.重载、覆盖(重写)、隐藏(重定义)
重载:
两个函数在同一作用域
函数名相同参数不同(类型个数顺序 )
重写(覆盖):
两个函数分别在基类和派生类的作用域
R_函数名/参数/返回值都必须相同(协变例外)
两个函数必须是虚函数
重定义(隐藏):子类和父类中有同名成员,直接访问时将屏蔽父类同名成员
两个函数分别在基类和派生类的作用域
函数名相同
两个基类和派生类的同名函数不构成重写就是重定义
2.6抽象类
通常多态中父类虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类称为抽象类
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
1.概念
包含纯虚函数的类叫做抽象类(接口类),抽象类不能实例化出对象。
派生类只有重写纯虚函数后才能实例化对象。
- 纯虚函数规范了派生类必须重写
- 纯虚函数体现出了接口继承
2.接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实
现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口【返回值函数名参数列表】,目的是为了重写,达成
多态,继承的是接口。如果不实现多态,不要把函数定义成虚函数。
3.代码演示
class Plant
{
public:
virtual void Breath() = 0;
};
class Rose :public Plant
{
public:
virtual void Breath()
{
cout << "Rose-疏影暗香" << endl;
}
};
class Grass :public Plant
{
public:
virtual void Breath()
{
cout << "Grass-生生不息" << endl;
}
};
void Test()
{
Plant* pRose = new Rose;
pRose->Breath();
Plant* pGrass = new Grass;
pGrass->Breath();
}
int main()
{
Test();
return 0;
}