我为用户提供了一个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 设计的标志:在正常用例中,调用虚拟方法足以调用正确的代码。但我认为在您的用例中可以安全地考虑它。