首页 > 解决方案 > C++ 多态继承问题的优雅解决方案

问题描述

想象一下我们正在使用的库有两个类 Dog 和 Bowl

class Bowl;

class Dog
{
    Bowl* m_bowl;
};

class Bowl
{
    Dog* m_owner;
};

Dog 拥有一个指向其关联的 Bowl 对象的指针。Bowl 还拥有一个指向其关联 Dog 对象的指针。一切都很好。我现在派生了一个新的类 FancyDog,它扩展了 Dog 的功能

class FancyDog : public Dog
{
     // Do the fancy dog stuff
};

一切都很好,至少在 FancyDog 需要用一个新的闪亮的豪华碗替换她无聊的、旧的普通碗之前。

class FancyBowl : public Bowl
{
    // Extra functions only relevant for FancyBowls
};

FancyDog 应该如何与其新的 FancyBowl 交互?我只能想到两个选项,这两个选项都“不错”,但都没有感到非常满意

  1. 除了她通过 Dog 继承的 Bowl 指针对象外,FancyDog 还拥有一个 FancyBowl 指针。从技术上讲,它们可能属于不同的类,但从概念上讲,FancyDog 现在拥有两个指向同一个对象的不同指针,这让我感觉不像是一个优雅的编码器。如果我们是迂腐的,我们也在浪费记忆,在大多数情况下,这无疑是微不足道的。

  2. 每次需要访问派生接口时,FancyDog 动态将其 Bowl 指针转换为 FancyBowl 指针。如果我们只需要这样做一两次,也许性能影响并不大,但同样不会让我感到温暖和模糊。

这只是一个经典的性能内存权衡还是有更优雅的解决方案?

PS 请注意,如果我们尝试让 FancyBowl 跟踪 Dog,我们将遇到同样的问题。

标签: c++inheritancepolymorphism

解决方案


这是 Dog/Bowl 作者设计不佳的问题。考虑以下改进:

class Bowl;

class Dog
{
    virtual Bowl* getBowl() = 0;
};

class Bowl
{
    Dog* m_owner;
};

现在 FancyDog 的实现可以持有一个 FancyBowl 并返回一个指向它的指针,这没问题。

本质上,这个问题是一个方差——因为 Dog 类保留分配m_bowl指针的权利,所以你永远不能保证它总是指向 FancyBowl,因为 Dog 可能会重新分配它。在第一种情况下,您保证 FancyDog 总是有一个 FancyBowl,但是您失去了 Dog 和 FancyDog 看到同一个碗的保证。在第二种情况下,您不知道碗将是 FancyBowl,但您知道您至少总是使用同一个碗。

如果 Dog 类声明了一个更小的接口,它只需要该类实际需要的内容(即您有一个 Bowl 可供它读取),那么问题就消失了。如果您不喜欢简单的运行时多态 getter,则上述还有更复杂的静态变体。


推荐阅读