多态:通俗来说,就是多种形态,具体点就是完成某个行为,当不同的对象去完成时会产生出不同的状态;
多态是在不同继承关系的类对象,去调用同一个函数,产生了不同的行为。
在继承中构成多态还有两个条件:
class Person{
public:
virtual void Test(){
cout << "Hello World" << endl;
}
};
虚函数的重写:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
class Person {
public:
virtual void BuyTicket() {
cout << "全" << endl;
}
};
class Studetn :public Person {
public:
virtual void BuyTicket() {
cout << "半" << endl;
}
};
void Useing(Person& p) {
p.BuyTicket();
}
void Test() {
Person PS;
Studetn ST;
Useing(PS);
Useing(ST);
}
虚函数重写存在两个列外:
1、协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。及基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变;
class A{};
class B :public A {};
class Person {
virtual A* f() {
return new A;
}
};
class Student :public Person {
public:
virtual B* f() {
return new B;
}
};
2、析构函数的重写(基类与派生类析构函数的名字不同)
如果基类的析构函数位虚函数,则此时派生类析构函数只要定义,无论是否加 virtual 关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不同,但是这里可以理解位编译器对析构函数做了特殊的处理,编译后析构函数的名称统一处理为 destructor。
class Preson {
public:
virtual ~Preson() {
cout << "~Preson" << endl;
}
};
class Student :public Preson {
public:
virtual ~Student() {
cout << "~Student" << endl;
}
};
void Test() {
Preson* p1 = new Preson;
Preson* p2 = new Student;
delete p1;
delete p2;
}
C++对函数重写要很严格,但是如果在编写程序的过程中出现了比如把函数名写错的这种错误,在编译期间是不会有提示的,只会时在程序的运行过程中出错。所以C++11引入了 override 和 final 这两个关键字进行检验,从而解决这个问题。
1、final:修饰虚函数,表示该虚函数不能再被继承
class Car {
public:
virtual void Drive() final { }
};
class Benz :public Car {
public:
virtual void Drive() {
cout << "dsa" << endl;
}
};
2、override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car {
public:
virtual void Drive() {
}
};
class Benz :public Car {
public:
virtual void Drive() override {
cout << "dsa" << endl;
}
};
再虚函数的后面写上 =0,则这个函数为纯虚函数;
包含纯虚函数的类叫做抽象类(也叫做接口类),抽象类不能实例化出对象!!派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能 实例化出对象,纯虚函数规范了派生类必须重写,另外春旭函数更加体现除了接口继承。
class Car {
public:
virtual void Drive() = 0;
};
class Benz :public Car {
public:
virtual void Drive() {
cout << "nice" << endl;
}
};
class BMW :public Car {
public:
virtual void Drive() {
cout << "using" << endl;
}
};
void Test() {
Car* pBenz = new Benz;
pBenz->Drive();
Car* pBMW = new BMW;
pBMW->Drive();
delete pBenz;
delete pBMW;
}
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的时函数的实现。
虚函数的继承是一种接口继承,派生类继承的时基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。
所以如果不是实现多态,就不要函数定义为虚函数。
看一段代码,求sizeof(Base)等于多少? ==>> 答案是8(VS2019 Debug x86)
class Base {
public:
virtual void Fun1() {
cout << "Fun1" << endl;
}
private:
int _b = 1;
};
void Test(){
Base b;
}
通过测试 b 对象是 8 bytes,除了 _b 成员,还多了一个 _vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个和平台有关),对象中的这个指针类型的 _vfptr 叫做虚函数表指针(v表示virtual,f代表function)。
一个含有虚函数的类中至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称为虚表。
这个表里到底放了什么?看一段代码:
class Base {
public:
virtual void Fun1() {
cout << "Fun1" << endl;
}
virtual void Fun2() {
cout << "Fun2" << endl;
}
virtual void Fun3() {
cout << "Fun3" << endl;
}
private:
int _b = 1;
};
class Derive :public Base {
public:
virtual void Fun1() {
cout << "Derive::Fun1" << endl;
}
private:
int _d = 2;
};
void Test(){
/*int i = sizeof(Base);
cout << i << endl;*/
Base b;
Derive d;
}
通过观察,可以得到以下几点:
还是以调试代码的形式来理解,我个人认为最可靠,也更加容易理解
class Preson {
public:
virtual void BuyTicket() {
cout << "全" << endl;
}
};
class student :public Preson {
public:
virtual void BuyTicket() {
cout << "半" << endl;
}
};
void Func(Preson& p) {
p.BuyTicket();
}
void Test(){
Preson Mike;
Func(Mike);
student John;
Func(John);
}
进入调试界面,实时监控 Mike 和 John 这两个对象:
可以看到,Mike对象实际在运行过程中调用的是 Preson::BuyTicket,而 John对象在实际运行过程中调用的是 重写的Student::BuyTicket。
结论:
静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态;(比如函数重载)
动态绑定又称为后期绑定(晚绑定),在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态;
class Base {
public:
virtual void Fun1() {
cout << "Base::Fun1" << endl;
}
virtual void Fun2() {
cout << "Base::Fun2" << endl;
}
private:
int a;
};
class Derive :public Base {
public:
virtual void Fun1() {
cout << "Derive::Fun1" << endl;
}
virtual void Fun3() {
cout << "Derive::Fun3" << endl;
}
virtual void Fun4() {
cout << "Derive::Fun4" << endl;
}
private:
int b;
};
void Test() {
Base b;
Derive d;
}
调试窗口:
在派生类虚函数表指针_vfptr中没有出现 Fun3 和 Fun4,是被编译器隐藏了,所以我们可以打印出所有虚函数的地址进行查看:
class Base1 {
public:
virtual void Fun1() {
cout << "Base1::Fun1" << endl;
}
virtual void Fun2() {
cout << "Base1::Fun2" << endl;
}
private:
int b1;
};
class Base2 {
public:
virtual void Fun1() {
cout << "Base2::Fun1" << endl;
}
virtual void Fun2() {
cout << "Base2::Fun2" << endl;
}
private:
int b2;
};
class Derive :public Base1, public Base2 {
public:
virtual void Fun1() {
cout << "Derive::Fun1" << endl;
}
virtual void Fun2() {
cout << "Derive::Fun3" << endl;
}
private:
int d1;
};
void Test() {
Derive d;
}
调试:
注意:多继承多态的派生类中没有重写的虚函数放在第一个继承基类部分的虚函数表中;