如何在子类中初始化具有自定义虚拟函数的基成员变量
How to initialize a base member variable with customized virtual function in the subclass
类StructComponent的构造函数采用不同的逻辑,根据信息的传入对象的类型初始化其成员变量。在这里,我使用强制转换将传入参数转换为右子类对象。
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>代码是否正确,忽略了潜在的设计问题?
Q2>假设ClassA的所有子类都具有函数CreateStructComponent的相同实现主体。有没有一种方法可以节省空间,不重复执行以下相同的代码:
return boost::shared_ptr<StructComponent> (new StructComponent(*this));
Q3>我可以使用更好的设计吗?例如,有没有一种方法可以忽略结构组件?
1)不,这是不正确的,至少,它没有达到你可能期望的效果。它在ClassA
的构造函数中调用一个虚拟函数,该函数将始终调用ClassA::CreateStructComponent()
,而不是调用派生类中的重写函数,因为当ClassA
构造函数运行时,动态类型为ClassA
。出于同样的原因,在StructComponent
的构造函数中,getType()
调用将始终解析为ClassA::getType()
。
2) 有很多方法可以解决这个问题。你可以把代码放在一个模板中,所以它取决于类型,或者你可以通过在不同的地方进行初始化来消除重复代码的需要。
3) 为什么不简单地给StructComponent
重载构造函数,一个取ClassB
,一个用ClassC
,另一个取ClassA
?
再说一遍,一个更好的解决方案可能是去掉StructComponent
构造函数,让ClassA
、ClassB
或ClassC
显式地进行初始化,这样每个类型都可以按照它希望的方式进行初始化。如果初始化取决于创建它的类型,则初始化不属于StructComponent
构造函数。目前您有一个循环依赖项,StructComponent
需要了解使用它的所有类型,所有使用它的类型都需要了解StructComponent
。这通常是设计出现问题的迹象,因为所有的类都是紧密耦合的。如果StrictComponent
对其他类型一无所知会更好。
无论如何,这里有一个可能的解决方案,展示了一种在不复制代码的情况下将正确类型传递给StructComponent
的方法,但我认为这不是一个好的设计。请注意,Structcomponent
构造函数被传递为NULL,因此它可以根据类型执行不同的操作,但不能访问它传递的对象。
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); }
};
还有另一个选择,这次没有循环依赖,将不同的初始化代码移动到它所属的位置:
struct StructComponent
{
StructComponent() { /* minimum init */ }
};
class ClassA
{
public:
ClassA() : m_shp( createA() ) { }
protected:
struct no_init { };
explicit
ClassA(no_init)
: m_shp()
{ }
boost::shared_ptr<StructComponent> createA()
{
// something
}
boost::shared_ptr<StructComponent> m_shp;
};
class ClassB : public ClassA
{
public:
ClassB()
: ClassA(no_init())
{ m_shp = createB(); }
private:
boost::shared_ptr<StructComponent> createB()
{
// something else
}
};
class ClassC : public ClassA
{
public:
ClassC()
: ClassA(no_init())
{ m_shp = createC(); }
private:
boost::shared_ptr<StructComponent> createC()
{
// something completely different
}
};
在执行类A
的构造函数期间,对象的类型为A
。因此,将始终调用基本实现。因此,您可以省去重新键入实现的麻烦。
此代码设计错误;设计错误的代码永远不会是正确的,至少在"正确"一词的通常含义中是这样。
如果C++规则不适合您,那么就没有理由使用构造函数进行初始化。只需将其作为一个方法,调用Initialize
,您就可以从Initialize
中调用任何您想要的虚拟方法,并获得您所期望的效果。
- C++无法定义虚拟函数 OUTER 类和头文件
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 当覆盖存在时调用基本虚拟"binded to object"函数
- 如何在C++中伪造虚拟可变参数函数模板?
- 类型擦除的std::function与虚拟函数调用的开销
- 重写虚拟函数和继承
- 是否可以使用函数指针调用虚拟析构函数?
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- 虚拟继承基构造函数消除
- "虚拟""覆盖"析构函数
- 类中的虚拟布尔函数参数不起作用
- 用纯虚拟函数兜圈子
- 将C++子类成员函数(虚拟实现)传递给 C 类型函数指针
- 尝试在 QLabel 上绘画失败(无法在没有对象的情况下调用成员函数"虚拟无效 QLabel::p aintEvent(QPaintEvent*)")
- 声明析构函数虚拟就足够了吗?
- 视觉 C++当我们在基类中使函数成为纯虚拟时,那么在子类中再次使相同的函数虚拟的必要性是什么
- 重载函数(虚拟/非虚拟)