C++ дизайн запрос
Я проектирую фреймворк на С ++, который должен обеспечивать базовую функциональность и действовать как интерфейс для других производных систем.
#include <stdio.h>
class Module
{
public:
virtual void print()
{
printf("Inside print of Module\n");
}
};
class ModuleAlpha : public Module
{
public:
void print()
{
printf("Inside print of ModuleAlpha\n");
}
void module_alpha_function() /* local function of this class */
{
printf("Inside module_alpha_function\n");
}
};
class System
{
public:
virtual void create_module(){}
protected:
class Module * module_obj;
};
class SystemAlpha: public System
{
public:
void create_module()
{
module_obj = new ModuleAlpha();
module_obj->print(); // virtual function, so its fine.
/* to call module_alpha_function, dynamic_cast is required,
* Is this a good practice or there is some better way to design such a system */
ModuleAlpha * module_alpha_obj = dynamic_cast<ModuleAlpha*>(module_obj);
module_alpha_obj->module_alpha_function();
}
};
main()
{
System * system_obj = new SystemAlpha();
system_obj->create_module();
}
Отредактировал код, чтобы он был более логичным, и он компилируется сразу. Вопрос в том, что есть лучший способ спроектировать такую систему, или dynamic_cast является единственным решением. Кроме того, если есть больше производных модулей, то для приведения типов требуется некоторый интеллект в базовом классе Module.
3 ответа
Дизайн становится намного чище, если Base
не содержит base_obj
объект, а скорее получает ссылку через виртуальный метод. Derived
должен содержать Derived2
объект, как:
class Base
{
public:
virtual void func1();
private:
class Base2;
virtual Base2& get_base2();
};
class Derived : public Base
{
Derived2 derived2;
public:
Base2& get_base2() { return derived2; }
void DerivedFunc()
{
derived2->Derived2Func();
}
}
Если вы беспокоитесь о производительности, передайте ссылку в конструкторе Base
,
Если Derived
является единственным конкретным примером Base
вы могли бы использовать static_cast
вместо.
Лично я определяю функцию, как MyCast
для каждого специализированного класса. Я определяю четыре перегруженных варианта, чтобы я мог понижать константные и неконстантные указатели и ссылки. Например:
inline Derived * MyCast(Base * x) { return static_cast<Derived *> (x); }
inline Derived const * MyCast(Base const * x) { return static_cast<Derived const *>(x); }
inline Derived & MyCast(Base & x) { return static_cast<Derived &> (x); }
inline Derived const & MyCast(Base const & x) { return static_cast<Derived const &>(x); }
И аналогично для Derived2 и Base2.
Большим преимуществом наличия всех четырех является то, что вы не будете изменять константу случайно, и вы можете использовать одну и ту же конструкцию независимо от того, есть ли у вас указатель или ссылка.
Конечно, вы могли бы заменить static_cast
с макросом, и использовать dynamic_cast
в режиме отладки и static_cast
это режим выпуска.
Кроме того, приведенный выше код можно легко обернуть в макрос, что упрощает пакетное определение функций.
Используя этот шаблон, вы можете реализовать свой код следующим образом:
class Derived : public Base
{
public:
virtual void func2()
{
base2_obj = new Derived2();
}
void DerivedFunc()
{
MyCast(base2_obj)->Derived2Func();
}
}
Я взял ваш код с множеством ошибок компиляции и попытался упростить его. Это то, что вы пытаетесь достичь? Это скомпилирует.
class Base2 {
public:
virtual void Derived2Func(){
}
};
Base2* fnToInstantiateABase2();
class Base {
public:
Base() : base2_obj(fnToInstantiateABase2()) {
}
virtual void DerivedFunc() {
}
protected:
Base2* base2_obj;
};
class Derived : public Base {
public:
void DerivedFunc() {
base2_obj->Derived2Func(); // not possible as base2_obj is of type Base2
}
};
class Derived2 : public Base2 {
public:
void Derived2Func() {
}
};
void test() {
Base * base_obj = new Derived();
base_obj->DerivedFunc();
}
Base2* fnToInstantiateABase2() {
return new Derived2();
}