如何在我的dll界面或ABI中使用标准库(STL)类?

在导出一个包含stl类的类与Visual Studio警告C4251有关的几个问题之前:例如这个问题或这个问题。 我已经阅读了UnknownRoad的优秀解释。

一味地禁用警告似乎有点危险,虽然它可能是一个选项。 包装所有这些std类并导出这些也不是一个真正的select。 毕竟它被称为标准模板库…也就是说,我们想要提供一个与这些标准类的接口。

我如何在我的dll界面中使用stl-classes? 什么是常见的做法?

在进一步阅读之前请记住一件事:我的答案是从编写可移植代码的angular度来看,这些代码可以在由不同编译器编译的模块组成的应用程序中使用。 这可以包括相同编译器的不同版本,甚至不同的补丁级别。

我如何在我的dll界面中使用stl-classes?

答:你经常不能1

原因: STL是一个代码库,而不是像DLL这样的二进制库。 它没有一个单一的ABI保证在你使用它的任何地方都是一样的。 事实上,STL确实代表“ 标准模板库”,但除标准外,这里还有一个关键的操作单词是模板

标准定义了每个STL类需要提供的方法和数据成员,并且定义了这些方法要做的事情; 但没有更多。 特别是,标准没有指定编译器编写者应该如何实现标准定义的function。 编译器编写者可以自由地提供一个STL类的实现,该STL类添加了标准中没有列出的成员函数和成员variables,只要标准定义的那些成员仍然在那里,并按照标准所说的做了什么。

也许一个例子是为了。 basic_string类在标准中定义为具有某些成员函数和variables。 标准中的实际定义几乎是4页,但是这里只是其中的一小部分:

 namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { [snip] public: // 21.3.3 capacity: size_type size() const; size_type length() const; size_type max_size() const; void resize(size_type n, charT c); void resize(size_type n); size_type capacity() const; void reserve(size_type res_arg = 0); void clear(); bool empty() const; [snip] }; 

考虑size()length()成员函数。 标准中没有指定成员variables来保存这些信息。 事实上,根本没有定义成员variables,甚至不保存string本身。 那么这是如何实施的?

答案是很多不同的方法。 一些编译器可能会使用size_t成员variables来保存大小,并使用char*来保存string。 另一个可能使用一个指向其他数据存储的指针来保存这些数据(这可能是在引用计数实现中的情况)。 实际上,相同编译器的不同版本甚至补丁级别可能会改变这些实现细节。 你不能依靠他们。 所以,MSVC 10的实现可能如下所示:

 namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { [snip] char* m_pTheString; }; size_t basic_string::size() const { return strlen(m_pTheString;) } 

… MSVC 10与SP1可能看起来像这样:

 namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string { [snip] vector<char> m_TheString; }; size_t basic_string::size() const { return m_TheString.size(); } 

我不是说他们看起来像这样,我只是说可能。 这里的要点是实际的实现是依赖于平台的,而且你真的无法知道它会在其他地方发生什么。

现在说你使用MSVC10编写一个导出这个类的DLL:

 class MyGizmo { public: std::string name_; }; 

什么是sizeof(MyGizmo)sizeof(MyGizmo)

假设我上面提出的实现,在MSVC10下将是sizeof(char*) ,但在SP1下它将是sizeof(vector<char>) 。 如果在使用DLL的VC10 SP1中编写应用程序,则对象的大小看起来与实际大小不同。 二进制接口已更改。


有关这方面的其他处理,请参阅C ++编码标准 (Amazon 链接 )问题#63。


1 :“ 你通常不能 ”当你完全控制工具链和库时,你实际上可以导出标准库组件或任何其他代码库组件(如Boost)。

根本的问题是,对于源代码库,不同编译器和不同版本的库的大小和定义可能是不同的。 如果你在一个使用代码的地方控制这些东西的环境中工作,那么你可能不会有任何问题。 例如,在所有系统都是内部编写的,只在内部使用的交易公司,可以这样做。