C++ 多态

发布于:2025-03-22 ⋅ 阅读:(80) ⋅ 点赞:(0)

一、多态的条件

1. ​定义

多态允许通过基类指针或引用,调用派生类对象的特定方法,实现“一个接口,多种实现”。

2. ​条件
  • 基类必须定义虚函数(virtual)​:虚函数是多态的核心机制。
  • 派生类必须重写(覆盖)基类的虚函数:派生类提供虚函数的具体实现(虚函数的重写要求继承父子关系的两个虚函数,函数,参数,返回值相同)。
  • 必须通过基类指针或引用调用虚函数:直接通过对象调用虚函数不触发多态(看的是基类指针指向的内容,如果是父类就调用父类的函数,子类就调用子类的函数)。
  • 注意:派生类重写可以不加virtual但是父类必须加 因为本质是重写了父类的实现,注意是重写了父类的实现,也就是说本质还是调用父类函数,但是内容变成了调用子类的函数内容
3. ​示例
class Animal {
public:
    virtual void speak() { cout << "Animal speaks" << endl; } // 虚函数
};

class Dog : public Animal {
public:
    void speak() override { // 重写基类虚函数(C++98 无需写 override)
        cout << "Dog barks" << endl;
    }
};

int main() {
    Animal* animal = new Dog();
    animal->speak(); // 输出 "Dog barks"(多态)
    delete animal;
    return 0;
}

二、重写与隐藏

1. ​重写(覆盖)​
  • 定义:派生类重新定义基类的虚函数,函数签名(函数名、参数列表、返回类型)必须一致(协变除外)。
  • 示例
    class Base {
    public:
        virtual void func(int x) { cout << "Base::func(int)" << endl; }
    };
    
    class Derived : public Base {
    public:
        void func(int x) { cout << "Derived::func(int)" << endl; } // 重写
    };
2. ​隐藏(重定义)​
  • 定义:派生类定义与基类同名的非虚函数(无论参数是否相同),将隐藏基类的同名函数。
  • 示例
    class Base {
    public:
        void func(int x) { cout << "Base::func(int)" << endl; }
    };
    
    class Derived : public Base {
    public:
        void func(double x) { cout << "Derived::func(double)" << endl; }
    };
    
    int main() {
        Derived d;
        d.func(1);    // 调用 Derived::func(double)(隐藏基类 func(int))
        d.Base::func(1); // 显式调用基类方法
        return 0;
    }

三、抽象类

1. ​定义
  • 抽象类包含至少一个 ​纯虚函数​,不能实例化。
  • 语法virtual 返回类型 函数名(参数) = 0;
  • 如果派生类继承了父类,而父类是一个抽象类则派生类也无法实例化,但是如果重写了纯虚函数的话,就能够实例化了,也就是说,纯虚函数间接的强制了派生类去重写这个虚函数
  • 实现继承:普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实 现。
  • 接口继承:虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成 多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数
2. ​示例
class Shape { // 抽象类
public:
    virtual double area() const = 0; // 纯虚函数
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() const override { // 必须实现纯虚函数
        return 3.14 * radius * radius;
    }
};

int main() {
    // Shape s; // 错误:抽象类不能实例化
    Shape* shape = new Circle(5.0);
    cout << shape->area(); // 输出圆的面积
    delete shape;
    return 0;
}

四、虚函数表

1. ​虚函数表
  • 每个有虚函数的类都有一个虚函数表,表中存储该类的虚函数地址。
  • 虚函数表在编译时生成,每个类的虚函数表唯一。
2. ​虚表指针​
  • 每个对象内部包含一个指向虚函数表的指针,在对象构造时初始化。
  • 调用虚函数时,通过指针找到虚函数表,再通过偏移量调用具体函数。
  • 注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。

五、override与final

1. override 关键字

作用:

  • 显式标记派生类中的虚函数是对基类虚函数的重写。
  • 修饰派生类的虚函数,检查是否完成重写,如果没完成重写就会报错。

  • 编译器检查:若函数签名与基类虚函数不一致(如参数、常量性不同),会报错,避免错误的重写。

示例:

class Base {
public:
    virtual void print() const {
        std::cout << "Base" << std::endl;
    }
};

class Derived : public Base {
public:
    // 正确:签名一致,重写基类虚函数
    void print() const override {
        std::cout << "Derived" << std::endl;
    }
};

class IncorrectDerived : public Base {
public:
    // 错误:参数不一致,override 触发编译错误
    void print(int) const override {
        std::cout << "IncorrectDerived" << std::endl;
    }
};

2. final 关键字

作用:

  • 修饰类:禁止类被继承。
  • 修饰虚函数:禁止派生类进一步重写该函数。
  • 可同时与 override 使用(顺序无关,如 override final)。
  • 修饰类时,放在类名后(class Base final {};)。
  • 修饰函数时,放在函数参数列表后(void foo() final {})。

示例:

修饰类:​

class Base final {}; // Base 不能被继承

// 错误:尝试继承 final 类
class Derived : public Base {};

修饰虚函数:​

class Base {
public:
    virtual void foo() final {} // 基类虚函数标记为 final
};

class Derived : public Base {
public:
    // 错误:无法重写 final 函数
    void foo() override {}
};

 


网站公告

今日签到

点亮在社区的每一天
去签到