Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何设计C++;用于二进制兼容扩展性的API 我设计了一个C++库的API,它将分布在一个DLL/共享对象中。该库包含具有虚拟函数的polymorhic类。我担心的是,如果我在DLL API上公开这些虚拟函数,我就无法用更多虚拟函数扩展相同的类,而不会破坏为以前版本的库构建的应用程序的二进制兼容性_C++_Virtual_Binary Compatibility - Fatal编程技术网

如何设计C++;用于二进制兼容扩展性的API 我设计了一个C++库的API,它将分布在一个DLL/共享对象中。该库包含具有虚拟函数的polymorhic类。我担心的是,如果我在DLL API上公开这些虚拟函数,我就无法用更多虚拟函数扩展相同的类,而不会破坏为以前版本的库构建的应用程序的二进制兼容性

如何设计C++;用于二进制兼容扩展性的API 我设计了一个C++库的API,它将分布在一个DLL/共享对象中。该库包含具有虚拟函数的polymorhic类。我担心的是,如果我在DLL API上公开这些虚拟函数,我就无法用更多虚拟函数扩展相同的类,而不会破坏为以前版本的库构建的应用程序的二进制兼容性,c++,virtual,binary-compatibility,C++,Virtual,Binary Compatibility,一种选择是使用习惯用法隐藏所有具有虚拟函数的类,但这似乎也有其局限性:这样应用程序就失去了对库的类进行子类化和重写虚拟方法的可能性 如何设计一个可以在应用程序中进行子类化的API类,而不丧失在新版本的dll中使用(不是抽象的)虚拟方法扩展API的可能性,同时保持向后二进制兼容 更新:库的目标平台是windows/msvc和linux/gcc。C++二进制兼容通常很困难,即使没有继承。以GCC为例。在过去的10年里,我不知道他们有多少次打破ABI的变化。那么MSVC有一套不同的约定,所以不能用GC

一种选择是使用习惯用法隐藏所有具有虚拟函数的类,但这似乎也有其局限性:这样应用程序就失去了对库的类进行子类化和重写虚拟方法的可能性

如何设计一个可以在应用程序中进行子类化的API类,而不丧失在新版本的dll中使用(不是抽象的)虚拟方法扩展API的可能性,同时保持向后二进制兼容


更新:库的目标平台是windows/msvc和linux/gcc。

C++二进制兼容通常很困难,即使没有继承。以GCC为例。在过去的10年里,我不知道他们有多少次打破ABI的变化。那么MSVC有一套不同的约定,所以不能用GCC链接到它,反之亦然。。。如果您将其与C世界相比较,编译器的互操作性似乎更好一些


如果你在Windows上,你应该看看COM。在引入新功能时,可以添加接口。然后调用者可以
QueryInterface()
让新的接口公开新的功能,即使最终改变了很多东西,也可以将旧的实现留在那里,或者为旧的接口编写垫片。

几个月前,我写了一篇名为“在GNU/Linux系统上实现C++共享库的二进制兼容性”[]。虽然Windows系统上的概念类似,但我确信它们不是完全相同的。但是,阅读了本文之后,您可以了解C++二进制级别上与兼容性有什么关系的概念。 顺便说一句,GCC应用程序二进制接口在标准文档草案“”中进行了总结,因此您将为选择的编码标准提供正式基础

举个简单的例子:在GCC中,如果没有其他类继承它,您可以用更多的虚拟函数扩展一个类


但无论如何,规则有时过于复杂而难以理解。因此,您可能会对验证两个给定版本(Linux)兼容性的工具感兴趣。

KDE知识库上有一篇有趣的文章,描述了在编写库时针对二进制兼容性的注意事项:

我想您错了理解子类化的问题

这是您的Pimpl:

// .h
class Derived
{
public:
  virtual void test1();
  virtual void test2();
private;
  Impl* m_impl;
};

// .cpp
struct Impl: public Base
{
  virtual void test1(); // override Base::test1()
  virtual void test2(); // override Base::test2()

  // data members
};

void Derived::test1() { m_impl->test1(); }
void Derived::test2() { m_impl->test2(); }

看到了吗?覆盖
Base
的虚拟方法没有问题,您只需要确保在
Derived
中重新声明它们
virtual
,这样从Derived派生的人就知道他们也可以重写它们(只有在您愿意的情况下,顺便说一句,这是为缺少它的人提供
final
的好方法),您仍然可以在
Impl
中为自己重新定义它,甚至可以调用
Base
版本

那里的
Pimpl
没有问题


另一方面,您会丢失多态性,这可能会带来麻烦。这取决于您决定是要使用多态性还是只使用组合。

如果您在头文件中公开PImpl类,则可以从它继承。您仍然可以保持向后可移植性,因为外部类包含指向PImpl对象的指针se如果库的客户端代码不是很明智,它可能会滥用这个公开的PImpl对象,并破坏二进制向后兼容性。您可以在PImpl的头文件中添加一些注释来警告用户。

“在过去10年中,我不确定他们有多少次破坏ABI更改”“让我告诉你有多少。一个。当前的ABI是正式的,并在标准文档中进行了描述。我知道2.95和3.0之间有一个重大的突破(这在BeOS和俳句上是一个严重的问题),但我似乎还记得3.2和3.3之间的另一个相当大的突破(这在Gentoo上造成了一些麻烦)。这不对吗?哦,我以为3.0已经超过10年了。是的,两个。2001年6月的一次,发布了3.0。从那时起,他们致力于生产一个良好的长寿命ABI设计,并在2002年8月的3.2版本中采用了它。七年前是最后一次。推荐COM解决二进制兼容性就像推荐氰化物治疗头痛一样。这两种方法都会通过杀死你:“All”来解决这个问题,但是,VisualC++的每个版本都会引入一个C运行库的不兼容的分支,其中一个DLL中的MALLC会在另一个DLL中自由地崩溃,而COM对象继续工作。这有助于避免您可能看到的误用,并了解它为您带来的好处。Pimpl的包装类应该具有非虚拟方法,因为在本例中,它正好用于隐藏库类的虚拟方法。如果库接口上存在虚拟方法,那么就不可能在新版本中使用更多虚拟方法扩展库接口,同时保持二进制兼容性。但是,如果发布的界面是非虚拟的,客户端将如何对其进行子类化?这就是帖子的内容。好吧,那我明白你的意思了。但这并不是Pimpl的问题。更多关于在接口中使用
virtual
方法的问题。“您只需要确保在派生中重新声明virtual,以便从派生中派生的方法也可以重写它们”。不,重写的虚拟方法也是隐式虚拟的。@Frank:对于编译器来说,它们是隐式虚拟的,对于读者来说,只有当它们被标记为隐式虚拟时,才是明显的(因为没有人想要)