假设我有以下类层次结构:
class Base
{
virtual int GetClassID(){ return 0;};
public:
Base() { SomeSingleton.RegisterThisObject(this->GetClassID());
}
class Derived
{
virtual int GetClassID(){ return 1;};
public:
Derived():Base(){};
}
嗯,这完全是从我的实际案例中简化出来的,但这是它的一般要点。
我想避免在每个派生类的构造函数中调用RegisterThisObject,所以我试图将调用移动到基类的构造函数。
在构造函数中不使用虚方法的情况下,是否可以使用任何模式来完成此操作?
答案 0 :(得分:8)
您可以使用奇怪的重复模板模式
template <class T>
class Base
{
protected: // note change
Base() { SomeSingleton.RegisterThisObject(T::GetClassID());
}
class Derived : Base<Derived>
{
static int GetClassID(){ return 1;};
public:
Derived(): Base<Derived>(){};
}
此外,如果您有多个代派生类(例如DerivedDerived : Derived
),则需要额外的工作。我建议您简单地避免,但在其他情况下,您可能希望将注册转移到策略类中(使行为可聚合而不是类标识的一部分)
扩展我的提示(使行为可聚合),你会看到类似这样的事情:
namespace detail
{
template <class T> struct registerable_traits { };
template<> struct registerable_traits<Derived>
{
enum _id { type_id = 1 };
};
}
template <class T>
class Base
{
protected: // note change
Base() { SomeSingleton::RegisterThisObject(detail::registerable_traits<T>::type_id); }
};
class Derived : Base<Derived>
{
public:
Derived(): Base<Derived>(){};
};
请参阅Codepad.org
答案 1 :(得分:7)
virtual
方法的问题在于它不起作用,因为在执行基础构造函数对象时,对象的类型是基础,而不是派生类型。
如果GetClassID
是静态成员函数,您可以更改设计,以便将标识符作为参数传递给基类型:
struct Base {
Base( int id ) {
register_object( id, this );
}
};
struct Derived {
static int getId() { return 5; }
Derived() : Base( getId() ) {}
};
答案 2 :(得分:3)
这个确切案例的最简单的解决方案就是将id作为一个传递
Base
的参数,并用它来完成。只要它只是一个
数据问题,不需要虚函数。更复杂
例如,您可以将指针传递给struct
,甚至是地址
(静态成员或免费)功能。
在更复杂的情况下,策略模式可能适用:实际 自定义被委托给单独的层次结构,并派生 类构造函数传递指向派生委托的指针。如果 委托没有状态,它可以是某个地方的静态实例。
最后,如果所有其他方法都失败了,您可以使用伪参数(如果派生的话 没有参数)或包装参数。这需要一些合作 来自派生类,并不适合临时工,但我已经 成功使用了一次或两次。基本上,你定义的东西 像:
class Base
{
public:
class DeferredInit
{
friend class Base;
mutable Base* myOwner;
public:
DeferredInit() : myOwner( NULL ) {}
~DeferredInit()
{
if ( myOwner != NULL ) {
myOwner->postCtor();
}
}
};
Base( DeferredInit const& initializer )
{
initializer.myOwner = this;
}
};
派生类类似于:
class Derived : public Base
{
public:
Derived( Base::DeferredInit const& fromAbove = Base::DeferredInit() )
: Base( fromAbove )
{
}
};
有一次我使用了这个,所有的课程都接受了std::string
作为输入,所以我安排DeferredInit
隐式转换
包装参数的std::string
和char const*
。再次,
客户端代码可以写:
Derived d( "some string" );
在完整表达式结束时调用了和postCtor
。 (那是
为什么这样的事情:
Derived( "abc" ).doSomething();
不行。您必须声明一个实例以确保postCtor
是
在以任何其他方式使用对象之前调用。没有临时工!)
但我只考虑这个解决方案作为最后的手段。它介绍 额外的复杂性,并增加了对临时的限制。
答案 3 :(得分:1)
我会将寄存器放入成员函数,并要求人们明确地调用它:
class Base {
void reg() { SomeSingleton.RegisterThisObject(GetClassID()); }
};
构造函数不是这种东西的正确位置:
int main() {
Derived d;
d.reg();
}