想象一下我们正在使用的库有两个类Dog和Bowl
class Bowl;
class Dog
{
Bowl* m_bowl;
};
class Bowl
{
Dog* m_owner;
};
狗拥有一个指向与其关联的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交互?我只能想到两个选项都“不错”,但都不令人满意。
FancyDog除了通过Dog继承的Bowl指针对象外,还拥有FancyBowl指针。从技术上讲,它们可能属于不同的类,但是从概念上讲,FancyDog现在拥有指向同一对象的两个不同的指针,这并不使我觉得自己像一个优雅的编码器。如果我们要学究的话,那也是在浪费记忆,在大多数情况下,这可以忽略不计。
FancyDog动态在每次需要访问派生接口时将其Bowl指针强制转换为FancyBowl指针。如果只需要执行一次或两次,也许对性能的影响并不大,但是这并没有让我感到温暖和模糊。
这仅仅是经典的性能记忆折衷方案吗?还是有更优雅的解决方案?
P.S。请注意,如果尝试使FancyBowl跟踪狗,就会遇到同样的问题。
答案 0 :(得分:4)
这是“狗/碗”作者设计不佳的问题。考虑以下改进:
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,则上述方法还有更复杂的静态变体。