C++继承-多态-虚函数 -抽象类-纯虚函数-接口 学习总结

612 阅读6分钟

c++子类重写父类的方法 父类方法必须使用虚函数吗?

C++ 子类重写父类的方法

在 C++ 中,子类重写父类的方法并不一定需要父类方法声明为虚函数,但如果您希望实现多态性,确实需要将父类的方法声明为虚函数。以下是详细说明:

1. 父类方法是否必须声明为虚函数?

  • 不是必须的:子类可以重写父类的方法,即使父类的方法没有被声明为虚函数。但是,这样做并不会实现多态性。
  • 多态性:如果您希望通过基类指针或引用来调用派生类中的重写方法,父类的相应方法必须声明为虚函数。

2. 子类重写是否必须加 override

  • 不是必须的:在子类中重写父类的方法时,并不强制要求使用 override 关键字。没有 override 也可以正常重写方法。
  • 建议使用 override:使用 override 是一种良好的编程习惯,它可以让编译器检查您是否正确重写了父类的方法。如果方法签名不匹配,编译器会提示错误,从而减少潜在的 bug。

示例代码

以下是一个简单的示例,展示了这两者的区别:

#include <iostream>

class Base {
public:
    void show() { // 非虚函数
        std::cout << "Base class show function" << std::endl;
    }
};

class Derived : public Base {
public:
    void show() { // 重写,但不使用 override
        std::cout << "Derived class show function" << std::endl;
    }
};

class AnotherBase {
public:
    virtual void display() { // 虚函数
        std::cout << "AnotherBase class display function" << std::endl;
    }
};

class AnotherDerived : public AnotherBase {
public:
    void display() override { // 重写,并使用 override
        std::cout << "AnotherDerived class display function" << std::endl;
    }
};

int main() {
    Base* b = new Derived();
    b->show(); // 调用 Base::show()

    AnotherBase* ab = new AnotherDerived();
    ab->display(); // 调用 AnotherDerived::display()

    delete b;
    delete ab;

    return 0;
}

抽象类和纯虚函数

在 C++ 中,抽象类和纯虚函数是面向对象编程的重要概念。它们用于定义接口并强制派生类实现特定的功能。以下是对这两个概念的详细说明:

抽象类

  • 定义:抽象类是不能被实例化的类,通常包含一个或多个纯虚函数。它的主要目的是提供一个接口,供派生类实现。
  • 特征
    • 抽象类可以包含普通成员函数和数据成员。
    • 任何包含至少一个纯虚函数的类都被视为抽象类。
    • 抽象类可以有构造函数和析构函数。

纯虚函数

  • 定义:纯虚函数是一种没有实现的虚函数,必须在派生类中被重写。它通过在函数声明末尾添加 = 0 来定义。
  • 语法
    class AbstractClass {
    public:
        virtual void pureVirtualFunction() = 0; // 纯虚函数
    };
    
    
  • 代码实例
#include<iostream>
using namespace std;
// 抽象类 Teacher
class Teacher{
public:
    string name;
    string shool;
    string major;

    virtual void goInClass() = 0; // 纯虚函数
    virtual void startTeaching() = 0; // 纯虚函数 
    virtual void afterTeaching() = 0; // 纯虚函数
};

// 抽象类 EnglishTeacher 继承自 Teacher
class EnglishTeacher : public Teacher{
public:
    void goInClass() override{ // 重写父类的纯虚函数
        cout << "英语老师开始进入教室" << endl;
    }

    void startTeaching() override{ // 重写父类的纯虚函数
        cout << "英语老师开始授课" << endl;
    }

    void afterTeaching() override{ // 重写父类的纯虚函数
        cout<<"英语老师下课!"<<endl;
    }
};

// 抽象类 ProTeacher 继承自 Teacher 
class ProTeacher : public Teacher{
public:
    void goInClass() override{ // 重写父类的纯虚函数
        cout << "专业老师开始进入教室" << endl;
    }

    void startTeaching() override{ // 重写父类的纯虚函数
        cout << "专业老师开始授课,并播放PPT" << endl;
    }

    void afterTeaching() override{ // 重写父类的纯虚函数
        cout<<"编程老师下课手把手教你敲代码"<<endl;
         } 
         
};
int main()
{
    // Teacher t;// 抽象类, 不支持实例化 子类必须实现抽象类的所有方法
    EnglishTeacher e;
    e.goInClass();
    ProTeacher t;
    t.startTeaching();
    t.afterTeaching();
    // 抽象类, 多态
    Teacher *teacher = new ProTeacher;
    teacher->startTeaching();
    return 0;
}

  • 代码运行结果

image.png

抽象类总结

定义

  • 抽象类是不能被实例化的类。
  • 抽象类通常包含一个或多个纯虚函数。

特点

  • 抽象类可以包含普通的成员函数和数据成员。
  • 任何包含至少一个纯虚函数的类都被视为抽象类。
  • 抽象类可以有构造函数和析构函数。

作用

  • 抽象类主要用于定义一个通用的接口或协议。
  • 它为派生类提供了一个基础,派生类必须实现抽象类中的纯虚函数。

纯虚函数

  • 纯虚函数是没有实现的虚函数,必须在派生类中被重写。
  • 通过在函数声明末尾添加 = 0 来定义纯虚函数。

多态

  • 通过基类指针或引用,可以调用派生类中重写的虚函数。
  • 这就实现了运行时多态,提高了代码的灵活性和可扩展性。

总之,抽象类是面向对象编程中的一个重要概念,它可以帮助我们定义通用接口,实现多态,并构建更加灵活和可扩展的软件系统。

虚函数与纯虚函数的区别

虚函数

  1. 定义:

    • 虚函数是在基类中声明的函数,用于实现多态。
    • 使用关键字 virtual 声明。
  2. 实现:

    • 虚函数可以在基类中有实现,也可以在派生类中重写。
    • 如果派生类没有重写,基类的实现会被使用。
  3. 实例化:

    • 基类可以被实例化,即使它包含虚函数。
  4. 用途:

    • 主要用于提供多态性,使得可以通过基类指针或引用调用派生类中的重写方法。

纯虚函数

  1. 定义:

    • 纯虚函数是没有实现的虚函数,声明时以 = 0 结尾。
    • 也使用关键字 virtual 声明。
  2. 实现:

    • 纯虚函数在基类中没有实现,必须在派生类中被重写。
    • 如果派生类没有重写纯虚函数,该派生类仍然是抽象类,不能实例化。
  3. 实例化:

    • 基类包含纯虚函数时,不能被实例化,称为抽象类。
  4. 用途:

    • 用于定义一个接口或协议,强制派生类实现特定的功能。

总结

  • 虚函数:可以有实现,允许基类被实例化,提供多态性。
  • 纯虚函数:没有实现,定义为抽象类,强制派生类实现,主要用于接口定义。

接口相关

定义

  • 接口是一种抽象的类型,用于定义一组方法的集合,描述对象应具备的行为。
  • 在 C++ 中,接口通常通过抽象类实现,包含一个或多个纯虚函数。

主要特点

  • 无实现: 接口仅提供方法的声明,不包含任何实现细节。
  • 多态性: 接口支持多态性,使得不同的类可以实现相同的接口,从而可以通过基类指针或引用调用这些类的方法。
#include <iostream>
#include <cmath>
using namespace std;

// 定义接口 Shape
class Shape {
public:
    // 纯虚函数,计算面积
    virtual double area() = 0;
    // 纯虚函数,计算周长
    virtual double perimeter() = 0;

};

// 实现 Circle 类
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() override {
        return M_PI * radius * radius; // πr²
    }

    double perimeter() override {
        return 2 * M_PI * radius; // 2πr
    }
};

// 实现 Rectangle 类
class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() override {
        return width * height; // w * h
    }

    double perimeter() override {
        return 2 * (width + height); // 2(w + h)
    }
};

// 主函数
int main() {
    Shape* circle = new Circle(5.0);
    Shape* rectangle = new Rectangle(4.0, 6.0);

    cout << "Circle Area: " << circle->area() << endl;
    cout << "Circle Perimeter: " << circle->perimeter() << endl;

    cout << "Rectangle Area: " << rectangle->area() << endl;
    cout << "Rectangle Perimeter: " << rectangle->perimeter() << endl;

    // 清理内存
    delete circle;
    delete rectangle;

    return 0;
}
  • 运行结果

image.png