C++ 多态继承问题的优雅解决方案
Posted
技术标签:
【中文标题】C++ 多态继承问题的优雅解决方案【英文标题】:Elegant Solution To The C++ Polymorphic Inheritance Problem 【发布时间】:2018-10-13 20:10:05 【问题描述】:假设我们使用的库有两个类 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 交互?我只能想到两个选项,都“不错”,但都没有很令人满意
除了她通过 Dog 继承的 Bowl 指针对象外,FancyDog 还拥有一个 FancyBowl 指针。从技术上讲,它们可能属于不同的类,但从概念上讲,FancyDog 现在拥有两个指向同一个对象的不同指针,这让我感觉不像是一个优雅的编码器。如果我们是迂腐的,我们也是在浪费记忆,在大多数情况下,这无疑是微不足道的。
FancyDog 每次需要访问派生接口时,都会将其 Bowl 指针动态转换为 FancyBowl 指针。如果我们只需要这样做一两次,也许性能损失没什么大不了的,但它不会让我感到温暖和模糊。
这只是一个经典的性能-内存权衡还是有更优雅的解决方案?
附:请注意,如果我们试图让 FancyBowl 跟踪 Dog,我们也会遇到同样的问题。
【问题讨论】:
不要使用原始指针是我能给出的最接近的建议。 FancyBowl 是 Bowl 的子级。制作任何需要区分 Bowl 和 FancyBowl 虚拟的方法有什么问题?这样你只需要保证 FancyDog 对象是用/给定的 FancyBowl 指针构造的。 老实说感觉更像是这种设计根本没有正确抽象。 也许简单的继承不能正确地模拟这种关系。 Dog 可以专门研究它的碗,并假设它至少有Bowl
的接口。
@πάνταῥεῖ 我查看了原始指针,但所有权/生命周期不是这里的问题。
【参考方案1】:
这是 Dog/Bowl 作者设计不佳的问题。考虑以下改进:
class Bowl;
class Dog
virtual Bowl* getBowl() = 0;
;
class Bowl
Dog* m_owner;
;
现在 FancyDog 的实现可以保存一个 FancyBowl 并返回一个指向它的指针,这没问题。
本质上,问题在于差异之一——因为 Dog 类保留 assign 到 m_bowl
指针的权利,你永远不能保证它总是指向 FancyBowl,因为Dog 可以重新分配它。在第一种情况下,您保证 FancyDog 总是有一个 FancyBowl,但是您失去了 Dog 和 FancyDog 看到同一个碗的保证。在第二种情况下,您不知道碗将是 FancyBowl,但您知道您至少总是使用同一个碗。
如果 Dog 类声明了一个更小的接口,它只需要该类实际需要的内容(即您有一个 Bowl 可供它读取),那么问题就消失了。如果您不喜欢简单的运行时多态 getter,上述还有更复杂的静态变体。
【讨论】:
解释得很清楚谢谢!在我的具体情况下,更改库需要做很多工作,但我确实学到了很多东西+你的名字太相关了!您是否有可能为您提到的静态变体提供链接或搜索关键字,以便我可以快速浏览一下,看看我是否能理解?以上是关于C++ 多态继承问题的优雅解决方案的主要内容,如果未能解决你的问题,请参考以下文章
C++ 继承多态关系中的赋值运算符的重载=operator()