yiy*_*c91 20 c++ dll stl instantiation visual-studio-2010
我的问题与导出带有STL的C++类有关.例如:
class __declspec(dllexport) Hello
{
std::string name;
public:
std::string& getName();
void setName(const std::string& name);
}
Run Code Online (Sandbox Code Playgroud)
各种文章似乎表明这是非常糟糕的,这是很容易理解的.必须使用相同的编译器设置和CRT版本编译所有内容.否则一切都会崩溃和燃烧.
我不明白为什么只有数据成员似乎有问题.使用下面的代码,我得到:" C4251:需要让dll接口被类的客户端使用 "; 这显然是通过导出实例化的std :: string来修复的:
struct __declspec(dllexport) SomeClass
{
// Removes the warning?
// http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html
// template class __declspec(dllexport) std::string;
std::string name; // Compiler balks at this
}
Run Code Online (Sandbox Code Playgroud)
固定版本是:
// Export the instantiations of allocator and basic_string
template class __declspec(dllexport) std::allocator<char>;
template class __declspec(dllexport) std::basic_string<char, std::char_traits<char>, std::allocator<char> >;
struct __declspec(dllexport) SomeClass
{
std::string name; // No more balking!
}
Run Code Online (Sandbox Code Playgroud)
(当您尝试使用DLL时,这将为LNK2005"basic_string已定义",这意味着您不必在客户端上的CRT中链接 - 因此它最终使用DLL中的实例化).
返回类型和参数似乎与STL没有问题,并且不会从编译器接收相同的处理数据成员.
// No exporting required?
struct __declspec(dllexport) SomeOtherClass
{
std::string doSomething1(); // No problemo
void doSomething2(const std::string& s); // No problemo
}
Run Code Online (Sandbox Code Playgroud)
同时:
class A {
std::string foo() { return std::string(); }
// std::string& foo(); gives the same result!
// std::string* foo(); also gives the same result!
}
class B {
std::string a;
}
Run Code Online (Sandbox Code Playgroud)
似乎都没有导出std :: basic_string或std :: allocator.相反,它们只导出类的成员/函数.
但是,问题中提到的固定版本会导出basic_string和allocator.
Han*_*ant 10
各种文章似乎表明这是非常糟糕的
是的,它可以.而你的项目设置会让你遇到他们警告的那种麻烦.按值公开C++对象需要DLL的客户端使用相同的CRT,以便客户端应用程序可以安全地销毁DLL中创建的对象.反过来说.这要求这些模块使用相同的堆.
并且您的项目设置会阻止编译警告的要点.您必须指定CRT的共享版本,以便所有模块加载CRT的一次性实现.
使用Project + Properties,C/C++,代码生成,运行时库设置进行修复.你现在在/ MT,它必须是/ MD.更改所有模块和所有配置.
这归结为某些东西是如何构建的。
当编译器看到
__declspec(dllimport) std::string f();
// ...
{
std::string tmp = f();
}
Run Code Online (Sandbox Code Playgroud)
它必须弄清楚要调用什么以及从哪里获取它。所以在这种情况下:
std::string tmp; => sizeof( std::string ), new (__stack_addr) std::string;
tmp = f(); => call f(), operator=( std::string )
Run Code Online (Sandbox Code Playgroud)
但因为它看到了 std::string 的完整实现,所以它只能使用相应模板的新实例。因此,它只需实例化 std::string 的模板函数即可,然后将函数合并留给链接器阶段,链接器在该阶段尝试找出可以将哪些函数折叠为一个函数。唯一未知的函数是 f(),编译器必须从 dll 本身导入它。(它被标记为他的外部)。
对于编译器来说,成员是一个更大的问题。它必须知道要导出的相应函数(构造函数、复制构造函数、赋值运算符、析构函数调用),并且当您将一个类标记为“dllexport”时,它必须导出/导入其中的每一个。您可以通过仅将必要的函数声明为 dllexport (ctor/dtor) 并禁止复制等方式,仅显式导出类的某些部分。这样,您就不必导出所有内容。
关于 std::string 的一个注意事项是,它的大小/内容在编译器版本之间发生变化,因此您永远无法在编译器版本之间安全地复制 std::string 。(例如,在 VC6 中,字符串有 3 个指针大,目前它是 16 个字节 + size + sizeof 分配器,我认为在 VS2012 中已对其进行了优化)。您永远不应该在界面中使用 std::string 对象。您可以创建一个 dll 导出的字符串实现,通过使用非导出的内联函数在调用方站点上将其转换为 std::string。
| 归档时间: |
|
| 查看次数: |
7169 次 |
| 最近记录: |