如何强制所有派生类实现虚方法?

发布于 2024-12-03 03:21:14 字数 562 浏览 2 评论 0原文

假设您有一个用于类树的基类 Dep。我希望每个叶类都实现一个虚拟方法 Dep* Dep::create() 。有什么办法可以强制执行吗?

注意:这里的问题是,可能有中间类(例如 class B : public A : public Dep)偶然或因为实现此方法(A::create)它们认为它们是叶类,但实际上它们自己是子类。

问题到这里就结束了。

上下文

如果你好奇我为什么需要这个;我有一个类 Master ,它具有未知具体类型的 Dep 对象。如果 Master 重复,我需要找到 Dep 实例的匹配克隆。接下来最好的事情是虚拟构造函数习惯用法,它恰恰引入了这个问题。

另外,我什至无法捕捉到这一点(除了可怕的崩溃),因为出于不明原因,比我有更多话要说的人在这个项目中禁止了 dynamic_cast (也许这是一个很好的决定;但无论如何,这是一个完全不同的讨论)。

Say you have a base class Dep for a tree of classes. There is a virtual method Dep* Dep::create() that I want to be implemented by every single leaf class. Is there any way to enforce this?

Note: The problem here is that there could be intermediate classes (say class B : public A : public Dep) implementing this method (A::create) by accident or because they think they are leaf classes, but are in fact subclassed themselves.

The question ends here.

Context

If you are curious why I need this; I have a class Master which has Dep objects of unknown concrete type. If Master is duplicated, I need to come up with a matching clone of the Dep instance. Next best thing to do is the virtual constructor idiom, which introduces precisely this problem.

Additionally, I cannot even catch this (other then by crashing horribly), because for obscure reasons, people that have more to say than me, have outlawed dynamic_cast in this project (perhaps this is a good decision; But anyways a completely different discussion).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

垂暮老矣 2024-12-10 03:21:14

C++ 没有提供任何方法来阻止类从您的类继承,也没有办法使继承层次结构中的特定类实现方法。唯一的规则是,在特定类之上的继承层次结构中的某个位置(不一定在叶中),所有虚拟函数都必须具有该类可实例化的实现。

例如,A 可以继承 Def 并实现它的所有[纯]虚拟方法。那么如果B继承自A,它就不必实现任何东西。没有办法阻止这种情况发生。

所以答案是否定的,没有办法强制执行。

C++ provides no way to keep a class from inheriting from your class, and there is no way to make a particular class in the inheritance hierarchy implement a method. The only rule is that somewhere in the inheritance hierarchy above a particular class (not necessarily in the leaf) all virtual functions must have an implementation for that class to be instantiatable.

For instance, A could inherit from Def and implement all it's [pure] virtual methods. Then if B inherits from A, it doesn't have to implement anything. There's no way to keep that from happening.

So the answer is no, there is no way to enforce this.

脱离于你 2024-12-10 03:21:14

使用奇怪的重复模板乐趣,您可以实现非常相似的目标:

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); }
};

使用 Cloneable::Create<,而不是从 Dep 派生并通过 new MyType 实例化/代码>。由于 Cloneable 派生自 MyType,因此您可以像使用任何 MyType 一样使用该实例,只不过它现在是保证有 Dep::clone

此外,您的 Master 不应接受 Dep 类型的实例,而应强制其为 Cloneable。 (用强制执行此操作的简单函数模板替换原始函数。)这保证了 master 内的任何 Dep 都具有正确实现的 clone 函数。

由于 Cloneable 没有公共构造函数,因此无法继承它,但是您的实际 MyType 可以像以前一样进一步继承和使用。

Using curiously recurring template fun, you can achieve something quite similar:

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); }
};

Instead of deriving from Dep and instantiating via new MyType, use Cloneable<MyType>::Create. Since Cloneable<MyType> is derived from MyType, you can use the instance the same way you would use any MyType, except that it is now guaranteed to have Dep::clone.

Additionally your Master should not accept an instance of type Dep, but enforce that it is a Cloneable<T>. (Replace your orignial function by a simple function template that enforces this.) This guarantees that any Dep inside the master has a correctly implemented clone function.

Since Cloneable<MyType> has no public constructor, it cannot be inherited, however your actual MyType can be further inherited and used just as before.

和影子一齐双人舞 2024-12-10 03:21:14

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()
}

Did TPTB outlaw all RTTI, or only dynamic_cast<>()? If you can use RTTI, then you can enforce the existence of the method as a postcondition of calling it:

#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()
}
治碍 2024-12-10 03:21:14

如果您控制基类 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

干杯&呵呵,,

If you control the base class AbstractDep then you can enforce that concrete leaf classes must be created by using a class template WithCloning. This leaf can then be sealed so that it cannot be inherited. Or more precisely, instances cannot be created of a derived class.

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;
}

If the classes require more than default construction then that most be solved additionally.

One solution is then to forward an argument pack from the WithCloning constructor (there is an example C++98 implementation on my blog, and C++0x supports that directly).

Summing up, to be instantiable the class must be WithCloning.

Cheers & hth.,

悸初 2024-12-10 03:21:14

当你说它们未知时,我认为它们仍然继承自公共基类/接口,对吗?

我能想到的唯一可以用来强制的是虚拟基类添加,

virtual Base* clone() {
    assert( 1==0 ); //needs to be overriden
}

因此您被迫覆盖,但这仅在运行时尝试在实例上调用克隆时检测到 缺少覆盖的类

即使您不允许使用dynamic_cast或RTTI,

,您仍然可以在构建上本地启用它以用于调试目的,如果这将帮助您找到有问题的类的typeid ,那么听起来您很熟悉克隆模式,但我会悄悄地将其发布在在这里,我们可以忘记它:
http://www.cplusplus.com/forum/articles/18757/

when you say that they are unknown, i presume they still inherit from a common base class /interface right?

only thing i can think of you can use to force is on the virtual base class add

virtual Base* clone() {
    assert( 1==0 ); //needs to be overriden
}

so you are forced to override, but this is only detected at run-time when trying to call clone on a instance of a class that is missing the override

even if you are not allowed to use dynamic_cast or RTTI, you can still enable it for debug purposes locally on your build, if that will help you find the typeid of offending classes

it sounds like you are familiar with the clone pattern, but i'll post it quietly in here, and we can forget about it:
http://www.cplusplus.com/forum/articles/18757/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文