C++多态性
类或对象的多态性(运行时多态)【重点】
定义
类的成员函数在编译时,无法确定调用的位置,只有在运行时,依据函数名和参数表确认调用的位置。
运行时多态的原理(了解)
虚函数表(_vfptr虚函数指针)
_vfptr虚函数指针
运行时,确定虚函数的位置
_vfptr指针指向虚函数表vftable
vftable实际上是函数指针数组
存储的是虚函数地址:基类指针或引用指向具体派生类重写的虚函数地址
个数比实际的虚函数多一个
前提条件
继承 + 重写基类的虚函数(virtual)
应用场景
父类的引用或指针 指向 子类的对象
override关键字
确定此成员函数是否正确的重写父类的成员虚函数
简单情况:
cpp
class Person {
public:
virtual void hi() {//虚函数
cout << "Person hi()" << endl;
}
};
class Student :public Person {
// 重写基类的虚函数
//override关键字:确定此成员函数是否正确的重写父类的成员虚函数
//重写虚函数的三同:返回类型、函数名、参数表
virtual void hi()override {
cout << "Student hi()" << endl;
}
};
int main(){
/*Person p1;
p1.hi();*/
//运行时多态的应用场景
Person* p1 = new Student();
p1->hi();//运行之后,调用的函数为Student对象的hi()
return 0;
}
多子类情况:
cpp
class Person {
public:
virtual void hi() {//虚函数
cout << "Person hi()" << endl;
}
};
class Student :public Person {
// 重写基类的虚函数
//override关键字:确定此成员函数是否正确的重写父类的成员虚函数
//重写虚函数的三同:返回类型、函数名、参数表
virtual void hi()override {
cout << "Student hi()" << endl;
}
};
class Worker :public Person {
public:
virtual void hi()override {
cout << "Worker hi()" << endl;
}
};
class Teacher :public Person {
public:
virtual void hi()override {
cout << "Teacher hi()" << endl;
}
};
void hello(Person &person) {
person.hi();//因为此处是Person类的引用,同时,hi()成员函数是虚函数
//因此编译时无法确认调用的位置,只有在程序运行后
//调用此函数传入具体的对象时,才能确定
}
int main() {
Person p1;
Worker w1;
Teacher t1;
Student s1;
//运行时多态的最终体现
hello(p1);
hello(w1);
hello(t1);
hello(s1);
return 0;
}

多态性练习:
cpp
// 练习: 动物世界
class Animal {
public:
virtual void eat() {
cout << "Animal eat... " << endl;
}
virtual void say() {
cout << "Animal say... " << endl;
}
};
// 完成 Dog和Cat的派生类
class Dog : public Animal {
// override 只是验证当前重写的虚函数是否OK
void eat() override {//不用加virtual也是虚函数
cout << "Dog eat 骨头 " << endl;
}
void say() override {//不用加virtual也是虚函数
cout << "Dog say 旺旺旺 " << endl;
}
};
class Cat : public Animal {
void eat() override {//不用加virtual也是虚函数
cout << "Cat eat 鱼 " << endl;
}
void say() override {//不用加virtual也是虚函数
cout << "Cat say 喵喵喵 " << endl;
}
};
// 运行时多态性: 一个对象具有多种形态
void animal_eat(Animal* animal) {
animal->eat();
}
void animal_say(Animal* animal) {
animal->say();
}
int main() {
animal_eat(new Dog());
animal_eat(new Cat());
animal_say(new Dog());
animal_say(new Cat());
return 0;
}
全局函数或同类中成员函数的多态性(静态多态、函数重载)
虚函数的使用规则
1.重写父类的虚函数时必须 3同:返回类型、函数名、参数列表
2.虚函数只修饰类的成员函数或析构函数,外部的全局函数时无法使用virtual关键字的
3.类的静态成员函数(静态成员没有this指针)、构造函数(构造函数是用来初始化成员的,在创建类对象时,必须确定位置)、拷贝构造(原因同构造函数)、inline内联函数、全局函数,无法为虚函数。
4.析构函数可以是虚函数,在运行时多态的情况下,必须虚函数化,因为实际回收的空间是子类对象。如果没有加virtual,则不会回收子类的对象空间
5.派生类中重写基类的虚函数时,可以不需要virtual关键字
6.基类指针或引用,指向派生类的对象
7.虚函数声明与定义分开时,在类外禁止使用virtual关键字
cpp
//重写父类的虚函数时必须 3同:返回类型、函数名、参数列表
//虚函数只修饰类的成员函数,外部的全局函数时无法使用virtual关键字的
//类的静态成员函数(静态成员没有this指针)、构造函数(构造函数是用来初始化成员的,在创建类对象时,必须确定位置)、拷贝构造(原因同构造函数),无法为虚函数。
class A {
public:
//virtual不能修饰static函数,因为静态成员没有this指针
static void hi() {
}
//virtual无法修饰构造函数,因为构造函数用来初始化成员的
A() {
}
//virtual无法使用,原因同构造函数
A(const A& other) {
}
//析构函数可以使用virtual,用于运行时多态的情况下回收具体的对象空间
virtual ~A() {
}
};
cpp
//析构函数可以使用virtual,用于运行时多态的情况下,回收具体的对象空间
class Person {
private:
int pid;
char *name;
public:
Person(int pid, const char* name) :pid(pid) {
this->name = new char[32] {0};
strcpy(this->name, name);
}
virtual void hi() {
cout << "Pid:" << pid << ",Name:" << name << endl;
}
virtual~Person() {
cout << "~Person()" << endl;
delete[]name;
}
};
class Student :public Person {
private:
char* clsName;//班级名
public:
Student(int pid, const char* name, const char* claName) : Person(pid, name) {
this->clsName = new char[32]{0};
strcpy(this->clsName, claName);
}
void hi() {
Person::hi();
cout << "clsName:" << clsName << endl;
}
~Student() {
cout << "~Student()" << endl;
delete[]clsName;
}
};
int main() {
Person* p = new Student(1001, "lucy", "2502");
p->hi();//只执行Person的析构函数,导致clsName的空间没有回收,需要给父类的析构函数前面加virtual
delete p;
return 0;
}
cpp
//虚函数声明与定义分开时,在类外禁止使用virtual关键字
class Person {
private:
int pid;
char *name;
public:
Person(int pid, const char* name) :pid(pid) {
this->name = new char[32] {0};
strcpy(this->name, name);
}
virtual void hi() {
cout << "Pid:" << pid << ",Name:" << name << endl;
}
//声明
virtual~Person();
};
//类外定义虚函数,禁止使用virtual
Person::~Person() {
cout << "~Person()" << endl;
delete[]name;
}
抽象类与接口类
抽象类
包含纯虚函数的类
纯虚函数
没有函数体(函数定义)
virtual 返回类型 函数名(参数表)=0;
例子:virtual void sayByte() = 0;
抽象类的特点
- 不能实例化(也是纯虚函数特点)
- 派生类未实现纯虚函数时,仍为抽象类
cpp
class A {
public:
virtual void hi() {
cout << "hi"<<endl;
}
virtual void sayByte() = 0;//纯虚函数
};
class B :public A {
};
class C :public B {
public:
void sayByte()override {
cout << "C bye Abstract Class" << endl;
}
};
int main() {
//A a;//抽象类不能创建对象
//B b;//抽象类的派生类未实现纯虚函数的功能,仍为抽象类
C c;
c.hi();
c.sayByte();
return 0;
}
接口类
类中所有的函数都是纯虚函数
作用
定义统一的行为规范、实现多继承、降低程序模块间耦合,提高系统的可扩展性与可维护性
cpp
#include<iostream>
#include<string>
using namespace std;
//设计打印机接口类 :只有功能声明,未实现
class Printer {
public:
virtual void print(string info) = 0;
virtual void cpy(string info) = 0;
};
//设计wifi打印机接口类
class WifiPrinter :public Printer {
public:
virtual void setWifi(string ssid, string pwd) = 0;
};
//定义Dell打印机的功能类
class DellPrinter :public WifiPrinter {
void print(string info)override {
cout << "DellPrinter print:" << info << endl;
}
void cpy(string info)override {
cout << "DellPrinter cpy:" << info << endl;
}
void setWifi(string ssid, string pwd) {
cout << "DellPrinter wifi:" << ssid << " " << pwd << endl;
}
};
class XiaomiPrinter :public WifiPrinter {
void print(string info)override {
cout << "XiaomiPrinter print:" << info << endl;
}
void cpy(string info)override {
cout << "XiaomiPrinter cpy:" << info << endl;
}
void setWifi(string ssid, string pwd) {
cout << "XiaomiPrinter wifi:" << ssid<<" "<< pwd << endl;
}
};
class SoniPrinter :public WifiPrinter {
void print(string info)override {
cout << "SoniPrinter print:" << info << endl;
}
void cpy(string info)override {
cout << "SoniPrinter cpy:" << info << endl;
}
void setWifi(string ssid, string pwd) override {
cout << "SoniPrinter wifi:" << ssid << " " << pwd << endl;
}
};
//体现运行时多态
void print(WifiPrinter& printer, string info) {
printer.print(info);//此处在编译时不能确定位置,只有在运行后,
//调用此函数时,传入的对象,依据插入对象的_vfptr确认位置
}
void cpy(WifiPrinter& printer,string info) {
printer.cpy(info);
}
void setWifi(WifiPrinter& printer,string ssid, string pwd) {
printer.setWifi(ssid,pwd);
}
int main()
{
DellPrinter p1;
XiaomiPrinter p2;
SoniPrinter p3;
print(p1, "hi,Disen");
cpy(p3, "I love C++");
setWifi(p2, "desen", "12345678");
return 0;
}