С++ скрытие функций-членов в иерархии наследования, начиная с CRTP

Вчера я написал некоторый код, и я был бы очень признателен за суждение, хорошая это или плохая практика. И если это плохо, что может пойти не так.

Конструкция следующая:

К сожалению, базовый класс A исходит из API в качестве шаблона. Цель состояла в том, чтобы иметь возможность поместить классы, производные от A, в std::vector. Я достиг этого с помощью базового класса B, от которого будут наследоваться все производные классы, унаследованные от A. Без чисто виртуальной функции в B была вызвана функция foo() из A.

С чисто виртуальной функцией в B вызывается версия foo() из C. Это именно то, что я хочу.

Так это плохая практика?

ИЗМЕНИТЬ:

Чтобы устранить некоторые недоразумения в моем примере кода:

template <class T> 
class A 
{
public:
    /* ctor & dtor & ... */
    T& getDerived() { return *static_cast<T*>(this); }
    void bar() 
    {
        getDerived().foo(); // say whatever derived class T will say
        foo(); // say chicken
    }
    void foo() { std::cout << "and chicken" << std::endl; }
};

class B : public A<B>
{
public:
    /* ctor & dtor */
    virtual void foo() = 0; // pure-virtual 
};

class C : public B
{
public:
    /* ctor & dtor */
    virtual void foo() { std::cout << "cow"; }
};

class D : public B
{
public:
    /* ctor & dtor */
    virtual void foo() { std::cout << "bull"; }
};

std::vector должен содержать как C, так и D и

void main()
{
std::vector<B*> _cows;
_cows.push_back((B*)new C()));
_cows.push_back((B*)new D()));

for(std::vector<B*>::size_type i = 0; i != _cows.size(); ++i)
   _cows[i].bar();

}

с выходом

cow and chicken
bull and chicken

желательно.

Насколько я знаю, нет другого способа хранить классы, производные от класса шаблона, в контейнере, кроме использования базового класса. Если я использую базовый класс для моих производных классов, например, B, я должен привести каждый экземпляр в векторе обратно к его надлежащему классу. Но во время вызова bar() я не знаю точного типа.


person Michael Haidl    schedule 11.06.2013    source источник
comment
В базовых классах нет виртуального деструктора.   -  person Mark Garcia    schedule 11.06.2013
comment
вы скрыли пустоту A::foo() в B. В A она не виртуальная   -  person spiritwolfform    schedule 11.06.2013
comment
@spiritwolfform хорошо, но разве это плохая практика? может ли что-то пойти ужасно не так с этим? и да, никаких виртуальных деструкторов, так как это отрывок, а не исходный код.   -  person Michael Haidl    schedule 11.06.2013
comment
это означает, что теперь у вас есть два метода, поэтому вы можете выполнить _cows[0].foo() для печати cow и _cows[0].A<B>::foo() для печати chicken, теперь это два независимых метода.   -  person spiritwolfform    schedule 11.06.2013
comment
@kronos: Зачем нужны функции virtual, когда вы используете CRTP, который в основном предназначен для статического полиморфизма? stackoverflow.com/questions/262254/   -  person legends2k    schedule 11.06.2013
comment
@legends2k спасибо за ссылку. теперь я знаю, по крайней мере, из чего состоит шаблон посетителя (в моем примере класса A). Настоящий A использует будочные реализации foo(). Однако без виртуальных функций (и без B) классы, производные от A, не будут помещаться в один контейнер.   -  person Michael Haidl    schedule 11.06.2013


Ответы (1)


Я называю это плохой практикой за то, что вы можете запутать и запутать вещи.

1) Имя функции во всех базовых и подклассах должно быть либо виртуальным, либо невиртуальным.
Смешивать переопределение и перегрузку в иерархии наследования — очень плохая идея.
Никто на самом деле не ожидает чего-то подобного.< br> Таким образом, если A::foo также должен быть виртуальным, или B и C foo не должны быть виртуальными.

2) Желательно, чтобы базовые классы были интерфейсами.
Таким образом, A:foo должен быть чисто виртуальным, а не B::foo.
2b) Если базовый класс уже предоставил реализацию по умолчанию, не объявляйте функцию pure виртуальный в подклассе.

person tlvs    schedule 11.06.2013