如何使用子类中的自定义虚函数初始化基本成员变量

时间:2012-07-31 19:26:47

标签: c++ design-patterns

StructComponent类的构造函数采用不同的逻辑来根据info的传入对象的类型初始化其成员变量。这里我使用强制转换将传入参数转换为右子类对象。

class StructComponent
{
public:
    StructComponent(const ClassA& info)
    {
        if (info.getType() == CLASS_B)
        {
            const ClassC& classC = dynamic_cast<const ClassC&> info;
            ...
            apply a different logic for ClassB and init member accordingly
        } else if (info.getType() == CLASS_C) {
            apply a different logic for others
            ...
        } else {
                    apply default
            }
    }
}

class ClassA
{
public:
    ClassA(...)
    {
        m_shp = CreateStructComponent();
    }   
    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }

    ...
    int getType() const { return CLASS_A; }

protected:
    boost::shared_ptr<StructComponent> m_shp;
}

class ClassB : public ClassA
{
public:
    ...

    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }   
    ...
    int getType() const { return CLASS_B; } 
}

class ClassC : public ClassA
{
public:
    ...

    virtual boost::shared_ptr<StructComponent> CreateStructComponent()
    {
        return boost::shared_ptr<StructComponent> (new StructComponent(*this));     
    }   
    ...
    int getType() const { return CLASS_C; } 
}

Q1&GT;代码是否正确无视潜在的设计问题?

Q2&GT; Assuem ClassA的所有子类都具有与CreateStructComponent函数相同的实现主体。 有没有办法可以节省空间,不要重复执行如下相同的代码:

return boost::shared_ptr<StructComponent> (new StructComponent(*this));

Q3&GT;我可以使用更好的设计吗?例如,有没有一种方法可以忽略铸造 StructComponent?

2 个答案:

答案 0 :(得分:2)

在执行类A的构造函数期间,对象的类型为A。因此将始终调用基本实现。因此,您可以省去重新输入实现的麻烦。

此代码设计不正确;错误设计的代码永远不会正确,至少在“正确”一词的通常含义中。

如果C ++规则不适合您,则没有理由使用构造函数进行初始化。只需将其设为方法,调用事物Initialize,然后您可以使用您期望的效果调用所需的Initialize虚拟方法。

答案 1 :(得分:2)

1)不,这是不正确的,至少,它不会做你所期望的。它在ClassA的构造函数中调用一个虚函数,它总是调用ClassA::CreateStructComponent()而不是调用派生类中的重写函数,因为当ClassA构造函数运行时,该动态类型为{ {1}}。出于同样的原因,在ClassA的构造函数中,StructComponent调用将始终解析为getType()

2)有很多方法可以解决这个问题。您可以将代码放在模板中,因此它取决于类型,或者您可以通过在不同的位置进行初始化来摆脱复制代码的需要。

3)为什么不简单地给ClassA::getType()重载的构造函数,一个服用StructComponent,一个服用ClassB,另一个服用ClassC

然后,一个更好的解决方案可能是摆脱ClassA构造函数并让StructComponentClassAClassB明确地进行初始化,以便每种类型都会初始化它的完成方式。如果初始化取决于创建它的类型,则初始化不属于ClassC构造函数。目前您有一个循环依赖,StructComponent需要知道使用它的所有类型,并且使用它的所有类型需要知道StructComponent。这通常表明设计存在问题,所有类都紧密相连。如果StructComponent对其他类型一无所知会更好。

无论如何,这是一个可能的解决方案,显示了一种将正确的类型传递给StrictComponent而无需复制代码的方法,但我不认为这是一个好的设计。请注意,StructComponent构造函数传递为NULL,因此它可以根据类型执行不同的操作,但不能访问它传递的对象。

Structcomponent

这是另一种完全不同的方法,没有虚拟基础攻击,仍然删除重复的代码,并允许class StructComponent { public: explicit StructComponent(const ClassA*) { /* something */ } explicit StructComponent(const ClassB*) { /* something else */ } explicit StructComponent(const ClassC*) { /* something completely different */ } }; class Base { protected: template<typename T> explicit Base(const T*) : m_shp( boost::make_shared<StructComponent>((T*)NULL) ) { } boost::shared_ptr<StructComponent> m_shp; }; class ClassA : virtual public Base { public: ClassA() : Base(this) { } }; class ClassB : public ClassA { public: ClassB() : Base(this) { } }; class ClassC : public ClassA { public: ClassC() : Base(this) { } }; 访问传递给它的对象(我维护这是一个坏主意):

StructComponent

这是另一个选择,这次没有循环依赖,移动它所属的不同初始化代码:

class StructComponent
{
public:
    explicit StructComponent(const ClassA& a)
    { /* something */ }

    explicit StructComponent(const ClassB& b)
    { /* something else */ }

    explicit StructComponent(const ClassC& c)
    { /* something completely different */ }
};

class ClassA
{
public:
    ClassA() : m_shp( create(*this) ) { }

protected:
    struct no_init { };

    explicit
    ClassA(no_init)
    : m_shp()
    { }

    template<typename T>
      boost::shared_ptr<StructComponent> create(const T& t)
      { return boost::make_shared<StructComponent>(t); }

    boost::shared_ptr<StructComponent> m_shp;
};

class ClassB : public ClassA
{
public:
    ClassB()
    : ClassA(no_init())
    { m_shp = create(*this); }
};

class ClassC : public ClassA
{
public:
    ClassC()
    : ClassA(no_init())
    { m_shp = create(*this); }
};