防止在C++中对抽象类接口进行子类化

Prevent subclassing an abstract class interface in C++

本文关键字:接口 子类 抽象类 C++      更新时间:2023-10-16

我为用户提供了一个SDK,允许他们编写DLL C++以扩展软件。

SDK 标头主要包含接口类定义。这些类有两种类型:

  • 用户必须子类和实现的一些
  • 有些是核心类的包装器,由应用作为指针传递给 DLL 函数,然后 DLL 代码可以将其用作调用核心函数的参数。这些接口不应由用户子类化并传递给核心函数,因为它们需要特定的核心子类。

我在手册中编写了不应被子类化的接口,并且只能通过应用程序提供的对象的指针使用。但在某些地方,如果您不阅读手册,则在 SDK 中对它们进行子类化太诱人了。

是否可以防止在 SDK 标头中对某些接口进行子类化?

只要

客户端不需要将指针用于任何用途,但将其传递回 DLL,您只需使用前向声明即可;不能从不完整的类型派生。 (当面对类似的最近,我全猪,设计了一种特殊的包装类型基于void* . 接口代码中有很多转换,但是除了将值传递回我。

如果有问题的类实现了客户端必须的接口同样使用,有两种解决方案。 首先是改变这一点,将每个成员函数替换为一个自由函数,该函数需要指向类型的指针,只需提供前向声明。 这其次是使用类似以下内容的内容:

class InternallyVisibleInterface;
class ClientVisibleInterface
{
private:
    virtual void doSomething() = 0;
    ClientVisibleInterface() = default;
    friend class InternallyVisibleInterface;
protected:      //  Or public, depending on whether the client should
                //  be able to delete instances or not.
    virtual ~ClientVisibleInterface() = default;
public:
    void something();
};

并在您的 DLL 中:

class InternallyVisibleInterface : public ClientVisibleInterface
{
protected:
    InternallyVisibleInterface() {}
    //  And anything else you need.  If there is only one class in
    //  your application which should derive from the interface,
    //  this is it.  If there are several, they should derive from
    //  this class, rather than ClientVisibleInterface, since this
    //  is the only class which can construct the
    //  ClientVisibleInterface base class.
};
void ClientVisibleInterface::something()
{
    assert( dynamic_cast<InternallyVisibleInterface*>( this ) != nullptr );
    doSomething();
}

这提供了两个级别的保护:第一,尽管派生直接从ClientVisibleInterface是可能的,这是不可能的结果类具有构造函数,因此它不能实例。 其次,如果客户端代码确实以某种方式作弊,如果他这样做,将出现运行时错误。

您可能不需要两种保护;其中一种应该足够。 私有构造函数将导致编译时错误,而不是运行时。 另一方面,没有它,你就不会甚至不得不InternallyVisibleInterface在分布式标头。

一旦开发者有了开发环境,他几乎可以做任何事情,你甚至不应该试图控制它。

恕我直言,您能做的最好的事情就是确定核心应用程序和扩展 DLL 之间的限制,并确保从这些 DLL 接收的对象或正确的类,如果不是,则中止并显示独特的消息。

使用 RTTI 和 typeid 通常是不受欢迎的,因为它通常是糟糕的 OOP 设计的标志:在正常用例中,调用虚拟方法足以调用正确的代码。但我认为在您的用例中可以安全地考虑它。