定义多态类

典型的例子是抽象形状类,然后可以将其导出为正方形,圆形和其他具体形状。

父类:

让我们从多态类开始:

class Shape {
public:
    virtual ~Shape() = default;
    virtual double get_surface() const = 0;
    virtual void describe_object() const { std::cout << "this is a shape" << std::endl; }  

    double get_doubled_surface() const { return 2 * get_surface(); } 
};

如何阅读这个定义?

  • 你可以使用关键字 virtual 通过引入的成员函数定义多态行为。在这里,get_surface()describe_object() 显然将以不同于圆形的方式实现。当在对象上调用函数时,将在运行时确定与对象的真实类对应的函数。

  • get_surface() 定义为抽象形状是没有意义的。这就是为什么功能跟随 = 0。这意味着该功能是纯虚函数

  • 多态类应始终定义虚拟析构函数。

  • 你可以定义非虚拟成员函数。当为对象调用这些函数时,将根据编译时使用的类来选择函数。这里以这种方式定义 get_double_surface()

  • 包含至少一个纯虚函数的类是抽象类。抽象类无法实例化。你可能只有抽象类类型的指针或引用。

派生类

一旦定义了多态基类,就可以派生它。例如:

class Square : public Shape {
    Point top_left;
    double side_length;
public: 
    Square (const Point& top_left, double side)
       : top_left(top_left), side_length(side_length) {}

    double get_surface() override { return side_length * side_length; }   
    void describe_object() override { 
        std::cout << "this is a square starting at " << top_left.x << ", " << top_left.y
                  << " with a length of " << side_length << std::endl; 
    }  
};

一些解释:

  • 你可以定义或覆盖父类的任何虚函数。函数在父类中是虚拟的这一事实使其在派生类中是虚拟的。无需再次告诉编译器关键字 virtual。但是建议在函数声明的末尾添加关键字 override,以防止由函数签名中未被注意的变化引起的细微错误。
  • 如果定义了父类的所有纯虚函数,则可以为该类实例化对象,否则它也将成为抽象类。
  • 你没有义务覆盖所有虚拟功能。如果符合你的需要,你可以保留父版本。

实例化的示例

int main() {

    Square square(Point(10.0, 0.0), 6); // we know it's a square, the compiler also
    square.describe_object(); 
    std::cout << "Surface: " << square.get_surface() << std::endl; 

    Circle circle(Point(0.0, 0.0), 5);

    Shape *ps = nullptr;  // we don't know yet the real type of the object
    ps = &circle;         // it's a circle, but it could as well be a square
    ps->describe_object(); 
    std::cout << "Surface: " << ps->get_surface() << std::endl;
}