如Common practice in dealing with warning c4251: class … needs to have dll-interface所述,在dll接口中使用stl-classes不是一个好习惯。给出了一个例子:
#include <iostream>
#include <string>
#include <vector>
class __declspec(dllexport) HelloWorld
{
public:
HelloWorld()
{
abc.resize(5);
for(int i=0; i<5; i++)
abc[i] = i*10;
str="hello the world";
}
~HelloWorld()
{
}
std::vector<int> abc;
std::string str;
};
编译此文件时,可以观察到以下警告:
warning C4251: 'HelloWorld::str' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'HelloWorld'
warning C4251: 'HelloWorld::abc' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'HelloWorld'
然后问题是我们如何在不使用STL类向量和字符串的情况下实现相同的功能。我能想到的一个实现如下:
class __declspec(dllexport) HelloWorld2
{
public:
HelloWorld2()
{
abc_len = 5;
p_abc = new int [abc_len];
for(int i=0; i<abc_len; i++)
p_abc[i] = i*10;
std::string temp_str("hello_the_world");
str_len = temp_str.size();
p_str = new char[str_len+1];
strcpy(p_str,temp_str.c_str());
}
~HelloWorld2()
{
delete []p_abc;
delete []p_str;
}
int *p_abc;
int abc_len;
char *p_str;
int str_len;
};
如您所见,在新实现中,我们使用int * p_abc替换向量abc和char * p_str来替换字符串str。我的问题是,是否有其他优雅的实现方法可以做同样的事情。谢谢!
答案 0 :(得分:24)
我认为你至少有5个可能的选择来解决这个问题:
您仍然可以为您的字段保留STL /模板类,并将它们用作参数类型,只要您将所有字段保密(无论如何这是一个很好的做法)。 Here是对此的讨论。要删除警告,您只需根据#pragma
语句使用。
您可以创建包含私有STL字段的小包装类,并将包装类类型的字段公开给公众,但这很可能只会将警告移到另一个地方。
您可以使用PIMPL惯用法在私有实现类中隐藏您的STL字段,这些字段甚至不会从您的库中导出,也不会在其外部显示。
或者您可以按照here所述的方式实际导出所有必需的模板类专精,如C4251警告中所述。简而言之,您必须在HelloWorld
类之前插入以下代码行:
...
#include <vector>
template class __declspec(dllexport) std::allocator<int>;
template class __declspec(dllexport) std::vector<int>;
template class __declspec(dllexport) std::string;
class __declspec(dllexport) HelloWorld
...
顺便说一下,这些导出的顺序似乎很重要:向量类模板在内部使用allocator类模板,因此必须在向量实例化之前导出分配器实例化。
直接使用内在函数可能是最糟糕的选择,但是如果你封装了你的字段
int *p_abc;
int abc_len;
类似于class MyFancyDataArray
和字段
char *p_str;
int str_len;
在class MyFancyString
之类的东西中,那么这将是一个更体面的解决方案(类似于第二点所描述的解决方案)。但这可能不是经常重新发明轮子的最佳习惯。
答案 1 :(得分:7)
或做最简单的事情,将__declspec
移动到您想要导出的唯一成员:
class HelloWorld
{
public:
__declspec(dllexport) HelloWorld()
{
abc.resize(5);
for(int i=0; i<5; i++)
abc[i] = i*10;
str="hello the world";
}
__declspec(dllexport) ~HelloWorld()
{
}
std::vector<int> abc;
std::string str;
};
答案 2 :(得分:6)
我不确定你想在这里解决哪个问题。有两个不同的问题:交叉编译器二进制兼容性和避免&#34;未定义的符号&#34;链接器错误。你引用的C4251警告谈到了第二个问题。这主要是一个非问题,特别是对于std::string
和std::vector
这样的SCL类。由于它们已在CRT中实施,只要您的应用程序的双方使用相同的CRT,一切都将“正常工作”。在这种情况下,解决方案是禁用警告。
交叉编译二进制兼容性OTOH,这是你链接的另一个stackoverflow问题中讨论的,是一个完全不同的野兽。为了解决这个问题,你基本上必须保留所有公共头文件,不要提及任何SCL类。这意味着您要么必须使用PIMPL,要么在任何地方使用抽象类(或者混合使用)。
答案 3 :(得分:3)