如果基类指针不能访问派生类成员函数,那么多态有啥方便呢?

Posted

技术标签:

【中文标题】如果基类指针不能访问派生类成员函数,那么多态有啥方便呢?【英文标题】:If base class pointer cannot access derived class member function, then what is so convenient about polymorphism?如果基类指针不能访问派生类成员函数,那么多态有什么方便呢? 【发布时间】:2019-10-03 21:33:14 【问题描述】:

要实现多态,我们需要使用一个基类指针指向一个派生类实例。多态性的一切都很好,除了,如果每个派生类都有一个或几个自己的成员函数怎么办?如果基类指针不能访问这些派生类的成员函数,那么多态有什么方便呢?

下面是一个例子。 “形状”是一个基类。 “square”和“circle”是两个派生类。

class shape 
public:
    virtual void getArea()=0;
;

class square: public shape 
private:
    int edge;
public:
    square()edge = 1;
    virtual void getArea()  //polymorphism
        cout << edge*edge << "\n";
    
    void getNumberOfEdge()  //a new member function
        cout << "4\n";
    
;

class circle: public shape 
private:
    int radius;
public:
    circle()radius = 1;
    virtual void getArea()  //polymorphism
        cout << 3*radius*radius << "\n";
    
    void getCurvature()     //a new member function
        cout << 1/radius << "\n";
    
;


int main()
    shape* arr[2] = 
        new square(),
        new circle()
    ;
    arr[0]->getArea();
    arr[1]->getArea();
    arr[0]->getNumberOfEdge();  //compiler error

getArea() 是实现多态性的一个很好的例子。但是访问派生类成员函数会给我编译器错误,我完全理解为什么。但从设计的角度来看,我们并不想为了每个派生类而在基类中添加一堆虚函数,对吧?

【问题讨论】:

当然,如果您尝试混合不同的东西(正方形和圆形),这不是很好,但是如果您有一堆相似的东西(所有不同类型的多边形),那么它会非常有效。多态只是编程工具箱中的一种工具 但是看起来为了这个目的(所有不同类型的多边形)我可以只使用一个类“多边形”并有几个实例,对吧?拥有一堆派生类的全部意义在于它们都有自己的特征,因此成员更多。 这是题外话,但您不应该多态地使用原始 C 样式数组。数组索引将使用基类的大小移动到正确的地址,但派生类通常可能比基类大。最好包含std::unique_ptr 的数组。 【参考方案1】:

基类中的功能应该在基类中。特定于特定派生类的功能应该在这些派生类中。 virtual 函数允许代码操作形状并执行对任何形状都有效的操作,而无需了解如何对现在和将来可能存在的每种可能的形状类型执行这些功能。

但是从设计的角度来看,我们并不想仅仅为了每个派生类而在基类中添加一堆虚函数,对吧?

如果这些操作对基类有意义,那么它们可能应该去那里。如果它们是特定于形状的,那么它们属于对该功能有意义的特定类。

假设您有一个使用形状但没有八边形派生类的系统。多态性的关键在于,今天可以编写代码,如果有人添加它们,以后可以在八边形上完美运行。

【讨论】:

【参考方案2】:

使用正确的类型,您可以很好地解决边缘问题的数量,将多态性带到边缘!

让我们添加一个类:

// a base interface for all Edged shapes
struct HasEdges 
   virtual void getNumberOfEdges() const = 0;
    // side note: I'd prefer this getter to return an int
    // but keeping it as in the example
;

现在对于 Square(但不是对于 Circle!)我们可以这样做:

class Square: public Shape, public HasEdges 
// ...
public:
    void getNumberOfEdges() const override  /* implement */ 
// ...
;

主要是这样的:

int main()
    shape* arr[2] = 
        new square(),
        new circle()
    ;
    arr[0]->getArea();
    arr[1]->getArea();
    // some edgy thing below
    HasEdges* hasEdges = dynamic_cast<HasEdges*>(arr[0]);
    // above returns null on failure
    if(hasEdges) hasEdges->getNumberOfEdges();

    // releasing memory...

【讨论】:

假设上述失败返回 null 无需假设。当无法进行转换时,dynamic_cast 将返回 nullptr 感谢您的回答。为什么我不能将 arr[0] 从形状转换为方形? 因为可能有另一种前卫的形状,例如三角形,我们要避免 if(cast-to-square-succeeds) ... else-if(cast-to-octagon-succeeds) 的列表【参考方案3】:

运行时多态性是指运行时的多态行为。 getArea() 定义的行为在运行时根据它指向的实例进行更改。 因此,您将多态性与静态编译问题混合在一起。基类没有成员函数名称getNumberOfEdge(),会出现编译错误。

如果你想调用特定的实现,为什么不让编译器看到它并转换它呢?

public:
    virtual void getArea()=0;
    template<typename T>
    const T * get() 
        return dynamic_cast<T *>(this);
    
;

现在您可以将其转换为任何派生类型:

auto square_instance = arr[0]->get<square>();
if (square_instance) 
    square_instance->getNumberOfEdge();

A different approach to run time polymorphism is suggested by Sean Parent.我喜欢它并且现在更频繁地尝试这种方法。

【讨论】:

'get' 方法可能应该执行 dynamic_cast,因为 reinterpret_cast 永远不会失败

以上是关于如果基类指针不能访问派生类成员函数,那么多态有啥方便呢?的主要内容,如果未能解决你的问题,请参考以下文章

多态虚函数表底层实现多重继承的问题及处理

实验五——类的多态,继承和派生2

c++中的虚函数有啥作用?

实验五 类的继承派生和多态

当有多个派生类时,如何使用基类指针访问派生类的函数成员?

基类与派生类的指针和成员函数调用原理