Элегантное решение проблемы полиморфного наследования C ++ - PullRequest
0 голосов
/ 13 октября 2018

Представьте, что в используемой нами библиотеке есть два класса 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. FancyDog владеет указателем FancyBowl в дополнение к объекту-указателю Bowl, который она наследует через Dog.Технически они могут принадлежать к разным классам, но концептуально FancyDog теперь содержит два разных указателя на один и тот же объект, что не позволяет мне чувствовать себя элегантным программистом.Если мы педантичны, мы также тратим впустую память, по общему признанию, незначительное количество в большинстве контекстов.

  2. FancyDog динамически приводит свой указатель Bowl к указателю FancyBowl каждый раз, когда ему требуется доступ к производному интерфейсу,Возможно, удар по производительности не имеет большого значения, если нам нужно сделать это только один или два раза, но опять же, это не заставляет меня чувствовать себя тепло и нечетко.

Это просто классикакомпромисс между производительностью и памятью или есть более элегантное решение?

PS Обратите внимание, что у нас будет такая же проблема, если мы попытаемся заставить FancyBowl следить за собакой.

1 Ответ

0 голосов
/ 13 октября 2018

Это проблема плохого дизайна со стороны автора Dog / Bowl.Рассмотрим следующее уточнение:

class Bowl;

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

class Bowl
{
    Dog* m_owner;
};

Теперь реализация FancyDog может удерживать FancyBowl и возвращать указатель на это без проблем.

По сути, проблема заключается в дисперсии, потому что собакакласс сохраняет за собой право назначать указателю m_bowl, вы никогда не сможете гарантировать, что он всегда будет указывать на FancyBowl, так как Dog может переназначить его.В первом случае вы гарантируете, что у FancyDog всегда есть FancyBowl, но вы теряете гарантию, что Dog и FancyDog видят одну и ту же чашу.Во втором случае вы не знаете, что чаша будет FancyBowl, но вы знаете, что по крайней мере всегда используете одну и ту же чашу.

Если класс Dog объявляет более минимальный интерфейс, который требует толькокласс действительно нуждается (то есть, что у вас есть Чаша, доступная для его чтения), тогда проблема исчезнет.Существуют и более сложные статические варианты вышеупомянутого, если вам не нравится простой полиморфный геттер во время выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...