C++多态

Posted by 川川的博客 on May 9, 2025

多态

virtual关键字

  • 可以修饰成员函数,为了完成虚函数重写,满足多态条件之一。

  • 可以在菱形继承中,去完成虚继承,解决数据冗余和二义性。

多态两个条件:

  • 虚函数的重写
  • 父类对象的指针或者引用去调用虚函数

多态:跟调用对象的类型无关,指向哪个对象就调用它的虚函数

虚函数重写

派生类虚函数与基类虚函数的返回值类 型、函数名字、参数列表完全相同,子类的虚函数重写了基类的虚函数

原理:虚函数地址存放到对象的虚函数表(虚表)中,多态指向谁调用本质是运行到对象虚表找要调用的虚函数。

 class Person {
 public:
    virtual void BuyTicket() { cout << "买票-全价" << endl; }
 };
 
class Student : public Person {
 public:
    virtual void BuyTicket() { cout << "买票-半价" << endl; }
    
    /*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写
    		(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),不建议这样使用*/
    
    /*void BuyTicket() { cout << "买票-半价" << endl; }*/
 };
 
void Func(Person& p)
 { p.BuyTicket(); }
 
int main()
 {
    Person ps;
    Student st;
    
    Func(ps);
    Func(st);
 
    return 0;
 }

协变:基类与派生类虚函数返回值类型是不同类型对象

//协变:基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用
class A{};
 class B : public A {};
 
class Person {
 public:
    virtual A* f() {return new A;}
 };
 
class Student : public Person {
 public:
    virtual B* f() {return new B;}
 };

析构函数:基类与派生类析构函数的名字不同

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的 析构函数构成重写,编译后析构函数的名称统一处理成destructor。

override和final帮助用户检查是否重写

override检查派生类是否重写了基类的某个虚函数,修饰虚函数重写

//如果没有重写编译报错
class Car{
 public:
    virtual void Drive(){}
 };
 
class Benz :public Car {
 public:
    virtual void Drive() override {cout << "Benz-舒适" << endl;}
 };

final修饰的虚函数表示不能在被重写

class Car
 {
 public:
    virtual void Drive() final {}
 };
 
class Benz :public Car
 {
 public:
    virtual void Drive() {cout << "Benz-舒适" << endl;}
 };
  • 重载:两个函数在同一作用域,函数名相同参数不同(个数,类型)

  • 重写两个虚函数分别在基类和派生类作用域,函数名参数返回值都相同,(协变/析构除外)

  • 重定义:两个分别在基类和派生类的同名函数(非virtual虚函数)

抽象类/接口类

定义:在虚函数的后面写上 =0 ,则这个函数为纯虚函数,包含纯虚函数的类就是抽象类,抽象类不能实例化出对象

派生类继承后也不能实例化出对象,派生类只有重写纯虚函数,派生类才能实例化出对象

 class Car
 {
 public:
 	//纯虚函数
    virtual void Drive() = 0;
 private:
 	int _circle;
 };

sizeof(Car)  32位下占8个字节  因为有虚函数表指针vftptr
 
 //子类重写父类纯虚函数后才能实例化
 class Benz :public Car
 {
 public:
    virtual void Drive()
    {
        cout << "Benz-舒适" << endl;
    }
 };

多态原理

image-20250617215428325

1.虚函数存在哪? 代码段

2.虚函数表存在哪?代码段(常量区)

一个类的不同对象共享该类的虚表

动态绑定和静态绑定

动态绑定: 动态的多态 运行时到虚表中找虚函数地址

静态绑定: 静态的多态 编译时确定函数地址

单继承和多继承的虚函数表

单继承

class Base { 

public :
	virtual void func1() { cout<<"Base::func1" <<endl;}
 	virtual void func2() {cout<<"Base::func2" <<endl;}
private :
 	int a;
 };

class Derive :public Base { 
public :
 	virtual void func1() {cout<<"Derive::func1" <<endl;}
 	virtual void func3() {cout<<"Derive::func3" <<endl;}
 	virtual void func4() {cout<<"Derive::func4" <<endl;}
private :
 	int b;
 };
 
 
 

image-20250617230338074

多继承

class Base1 {
 public:
     virtual void func1() {cout << "Base1::func1" << endl;}
     virtual void func2() {cout << "Base1::func2" << endl;}
 private:
 	 int b1;
 };
 class Base2 {
 public:
     virtual void func1() {cout << "Base2::func1" << endl;}
     virtual void func2() {cout << "Base2::func2" << endl;}
 private:
 	 int b2;
 };
 
class Derive : public Base1, public Base2 {
public:
 	 virtual void func1() {cout << "Derive::func1" << endl;}
 	 virtual void func3() {cout << "Derive::func3" << endl;}
 private:
     int d1;
 };

多继承派生类未重写的虚函数放在第一个继承基类部分的虚函数表中

image-20250617230309854