Я могу представить себе несколько отличий:
С помощью виртуального базового класса вы нарушаете семантику, которую люди ожидают от классов C ++ с хорошим поведением:
Я ожидаю (или требую,даже) класс, который должен быть создан в стеке, например:
BananaTree myTree("somename");
в противном случае я теряю RAII, и мне приходится вручную отслеживать отслеживание выделений, что приводит к множеству головных болей и утечек памяти.
Я также ожидаю, что для копирования класса я могу просто сделать это
BananaTree tree2 = mytree;
, если, конечно, копирование не запрещено, помечая конструктор копирования как приватный, и в этом случае эта строка не будетдаже компиляция.
В приведенных выше случаях у нас, очевидно, есть проблема, заключающаяся в том, что в вашем классе интерфейса нет конструктивных конструкторов.Но если бы я попытался использовать код, такой как приведенные выше примеры, я бы также столкнулся с множеством проблем с нарезкой.В случае полиморфных объектов обычно требуется указатели или ссылки на объекты, чтобы предотвратить нарезку.Как и в первом пункте, это, как правило, нежелательно и значительно усложняет управление памятью.
Понимает ли читатель вашего кода, что BananaTree
в принципе не работает, что он должен использовать BananaTree*
или BananaTree&
вместо этого?
По сути, ваш интерфейс просто не очень хорошо работает с современным C ++, где мы предпочитаем
- избегать указателей в максимально возможной степени, и
- Распределение в стеке всех объектов для автоматического управления временем жизни.
Кстати, ваш виртуальный базовый класс забыл о виртуальном деструкторе.Это явная ошибка.
Наконец, более простой вариант pimpl, который я иногда использую, чтобы сократить объем стандартного кода, состоит в предоставлении «внешнему» объекту доступа к элементам данных внутреннего объекта, поэтомуВы избегаете дублирования интерфейса.Либо функция на внешнем объекте просто получает доступ к необходимым данным из внутреннего объекта напрямую, либо вызывает вспомогательную функцию для внутреннего объекта, которая не имеет эквивалента для внешнего объекта.
В вашем примере выможно удалить функцию и Impl::getBanana
, и вместо этого реализовать BananaTree::getBanana
следующим образом:
Banana* BananaTree::getBanana(string const& name)
{
return pimpl_->findBanana(name);
}
тогда вам нужно будет реализовать только одну getBanana
функцию (в классе BananaTree
) и одну findBanana
функция (в классе Impl
).