假设您有一个类树的基类Dep
。我希望每个叶子类都实现一个虚方法Dep* Dep::create()
。有没有办法强制执行此操作?
注意:这里的问题是可能有中间类(比如class B : public A : public Dep
)意外地实现了这个方法(A::create
),或者因为他们认为他们是叶子类,但实际上是自己的子类。
问题在此结束。
如果你很好奇为什么我需要这个;我有一个类Master
,它有Dep
个未知具体类型的对象。如果重复Master
,我需要找到Dep
实例的匹配克隆。接下来要做的最好的事情是虚拟构造函数idiom,它精确地引入了这个问题。
此外,我甚至无法抓住这一点(其他因为可怕的崩溃),因为出于晦涩的原因,比我更有说服力的人在这个项目中取缔dynamic_cast
(也许这是一个好的决定;但无论如何都是一个完全不同的讨论。)
答案 0 :(得分:3)
C ++没有办法让类继承自类,并且无法在继承层次结构中使特定的类实现方法。唯一的规则是在特定类(不一定在叶子中)的继承层次结构中某处所有虚函数必须具有该类可实例化的实现。
例如,A
可以从Def
继承并实现它的所有[纯]虚方法。然后,如果B
继承自A
,则不必实现任何内容。没有办法阻止这种情况发生。
所以答案是否定的,没有办法强制执行。
答案 1 :(得分:3)
使用奇怪的重复模板乐趣,你可以实现非常相似的东西:
template<typename T>
class Cloneable : public T, public Dep
{
private:
Cloneable<T>() : T() { }
public:
static Cloneable<T>* Create() { return new Cloneable<T>(); }
Cloneable<T>* clone() { return new Cloneable<T>(*this); }
};
使用Dep
,而不是从new MyType
派生并通过Cloneable<MyType>::Create
实例化。由于Cloneable<MyType>
派生自MyType
,因此您可以像使用任何MyType
一样使用该实例,但现在保证它具有Dep::clone
。
此外,您的Master
不应接受Dep
类型的实例,但强制它是Cloneable<T>
。 (用强制执行此操作的简单函数模板替换您的orignial函数。)这可以保证master中的任何Dep
都具有正确实现的clone
函数。
由于Cloneable<MyType>
没有公共构造函数,因此无法继承它,但您的实际MyType
可以像以前一样进一步继承和使用。
答案 2 :(得分:2)
TPTB是否取缔所有 RTTI,或仅dynamic_cast<>()
?如果你可以使用RTTI,那么你可以强制该方法的存在作为调用它的后置条件:
#include <typeinfo>
#include <cassert>
#include <iostream>
#include <stdexcept>
class Base {
protected:
virtual Base* do_create() = 0;
virtual ~Base() {}
public:
Base* create() {
Base *that = this->do_create();
if( typeid(*this) != typeid(*that) ) {
throw(std::logic_error(std::string() +
"Type: " +
typeid(*this).name() +
" != " +
typeid(*that).name()));
}
return that;
}
};
class Derive1 : public Base {
protected:
Base* do_create() { return new Derive1(*this); }
};
class Derive2 : public Derive1 {};
void f(Base*p) { std::cout << typeid(*p).name() << "\n"; }
int main() {
Derive1 d1;
Base *pD1 = d1.create(); // will succeed with correct semantics
Derive2 d2;
Base *pD2 = d2.create(); // will throw exception due to missing Derive2::do_create()
}
答案 3 :(得分:2)
如果您控制基类AbstractDep
,则可以强制执行必须使用类模板WithCloning
创建具体叶类。然后可以密封该叶子,使其不能被遗传。或者更准确地说,不能创建派生类的实例。
class AbstractDep
{
template< class Type > friend class WithCloning;
private:
enum FooFoo {};
virtual FooFoo toBeImplementedByLeafClass() = 0;
public:
virtual AbstractDep* clone() const = 0;
};
template< class Type > class WithCloning;
class Sealed
{
template< class Type > friend class WithCloning;
private:
Sealed() {}
};
template< class Type >
class WithCloning
: public Type
, public virtual Sealed
{
private:
AbstractDep::FooFoo toBeImplementedByLeafClass()
{
return AbstractDep::FooFoo();
}
public:
virtual WithCloning* clone() const
{
return new WithCloning( *this );
}
};
typedef WithCloning<AbstractDep> Dep;
class AbstractDerivedDep
: public AbstractDep
{
// AbstractDep::FooFoo toBeImplementedByLeafClass(); // !Not compile.
public:
};
typedef WithCloning<AbstractDerivedDep> DDep;
struct Foo: Dep {}; // !Does not compile if instantiated.
int main()
{
Dep d;
//Foo f;
}
如果类需要的不仅仅是默认构造,那么大多数都需要另外解决。
然后一个解决方案是从WithCloning
构造函数转发一个参数包(我的博客上有一个示例C ++ 98实现,而C ++ 0x直接支持它。)
总结一下,要实例化,该类必须是WithCloning
。
干杯&amp;第h。,
答案 4 :(得分:0)
当你说它们不为人知时,我认为它们仍然从公共基类/接口继承?
只有我能想到你可以使用强制在虚拟基类上添加virtual Base* clone() {
assert( 1==0 ); //needs to be overriden
}
因此您被迫覆盖,但只有在尝试在缺少覆盖的类的实例上调用clone时才会在运行时检测到
即使您不允许使用dynamic_cast或RTTI,您仍然可以在构建本地启用它以进行调试,如果这样可以帮助您找到违规类的类型
听起来你熟悉克隆模式,但我会在这里安静地发布它,我们可以忘掉它: http://www.cplusplus.com/forum/articles/18757/