在dll接口中使用静态类时消除C4251警告的一种方法
One way of eliminating C4251 warning when using stl-classes in the dll-interface
在dll-interface中使用still -classes不是一个好的做法,处理警告c4251: class…的常见做法需要有dll-interface解释。给出了一个例子:
#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类vector和string的情况下实现相同的功能。我能想到的一种实现如下:
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,我的问题是是否有其他优雅的实现方法可以做同样的事情。谢谢!
我认为你至少有5种可能的选择来解决这个问题:
-
你仍然可以为你的字段保留STL/模板类,也可以使用它们作为参数类型,只要你保持所有字段的私有(这是一个很好的做法)。这里有一个关于这个的讨论。要删除警告,您可以简单地使用
#pragma
语句。 -
您可以创建带有私有STL字段的小型包装器类,并将包装器类类型的字段公开给公众,但这很可能只会将警告移动到其他地方。
-
您可以使用PIMPL习惯用法将STL字段隐藏在私有实现类中,这些字段甚至不会从库中导出,也不会在库外可见。
-
或者您实际上可以导出所有必需的模板类专门化,正如C4251警告中建议的那样,采用下面描述的方式。简而言之,您必须在
HelloWorld
类之前插入以下代码行:
顺便说一下,这些导出的顺序似乎很重要:vector类模板在内部使用分配器类模板,因此分配器实例化必须在 vector实例化之前导出。... #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 ...
-
直接使用内部函数可能是你最糟糕的选择,但是如果你封装了你的字段
int *p_abc; int abc_len;
在
字段class MyFancyDataArray
和char *p_str; int str_len;
在
class MyFancyString
之类的东西中,那么这将是一个更体面的解决方案(类似于第二点所描述的解决方案)。但是,过于频繁地重新发明轮子可能不是最好的习惯。
或者做最简单的事情,将__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;
};
我不知道这里你想解决哪个问题。有两个不同的问题:交叉编译器二进制兼容性和避免"未定义符号"链接器错误。你引用的C4251警告提到了第二个问题。这真的是一个主要的非问题,特别是与SCL类,如std::string
和std::vector
。因为它们是在CRT中实现的,只要应用程序的双方使用相同的CRT,一切都将"正常工作"。在这种情况下,解决方案就是禁用警告。
交叉编译器二进制兼容性OTOH,这是你链接的另一个stackoverflow问题中讨论的,是一个完全不同的野兽。为了解决这个问题,基本上必须保持所有公共头文件不提及任何SCL类。这意味着您要么必须PIMPL所有内容,要么到处使用抽象类(或两者的混合)。
使用指针指向实现(pImpl)的习惯用法
- 处理编译器关于可能丢失数据的警告的最优雅方法是什么
- G++ 编译器未为未定义的方法生成错误/警告
- 从 int 中剥离位时,编译器会警告一个转换,但不警告其他转换.有解决方法吗?
- 对列表类中的泛型方法禁用编译器警告 2100,该泛型方法可能包含指针,也可能不包含指针
- GCC 7,-隐式故障警告,以及清除它们的便携式方法
- 是否有跨平台的方法可以在C++中禁用已弃用的警告?
- 将赋值运算符与make_pair方法一起使用会生成 CLion 警告
- 为什么方法重载或枚举标志定义会触发 gcc7.2 编译器警告?
- 在C++中将已签名/未签名的警告静音的侵入性最小的方法是什么?
- C :覆盖不弃用的虚拟方法时的贬值警告
- 有什么方法可以为函数指针比较生成警告?
- 在 Clang 中禁用特定警告实例的简洁方法
- 来自 Boost 的 JSON 解析器的警告 C4512 的解决方法
- 修复警告"field a is not used"如果字段在配置中未使用的好方法
- 由于未签名,以模板方法的方式摆脱警告
- 是否有一种方法可以找到导致警告出现在QT中的线路
- std::error_code 是发出警告的好方法吗?
- 重写的虚拟方法上的警告不明确
- 在 C++ 中修复类型转换警告的最佳方法
- 为什么 G++ 不为不返回任何内容的模板方法生成警告?