在 dll 接口中使用 stl 类时消除 C4251 警告的一种方法

2021-12-14 00:00:00 c++ visual-studio-2010

在 dll 接口中使用 stl 类不是一个好习惯,如 处理警告 c4251 的常见做法:class ...需要有 dll-interface 解释.给出一个例子:

#include #include <字符串>#include <向量>类 __declspec(dllexport) HelloWorld{上市:你好世界(){abc.resize(5);for(int i=0; i<5; i++)abc[i] = i*10;str="你好世界";}?你好世界(){}std::vectorABC;std::string str;};

编译此文件时,可以观察到以下警告:

 警告 C4251: 'HelloWorld::str' : class 'std::basic_string<_Elem,_Traits,_Ax>'需要有 dll 接口供HelloWorld"类的客户端使用警告 C4251:HelloWorld::abc":类std::vector<_Ty>"需要有 dll 接口供HelloWorld"类的客户端使用

那么问题是我们如何在不使用 STL 类向量和字符串的情况下实现相同的功能.我能想到的一种实现如下:

class __declspec(dllexport) HelloWorld2{上市:你好世界2(){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 = 新字符[str_len+1];strcpy(p_str,temp_str.c_str());}~HelloWorld2(){删除 []p_abc;删除 []p_str;}int *p_abc;国际 abc_len;字符 *p_str;int str_len;};

如您所见,在新的实现中,我们使用 int *p_abc 替换向量 abc 和 char *p_str 替换字符串 str.我的问题是是否有其他优雅的实现方法可以做到这一点.谢谢!

解决方案

我认为您至少有 5 种可能的选择来解决这个问题:

  1. 您仍然可以为您的字段保留 STL/模板类,也可以将它们用作参数类型,只要您将所有字段保持为私有(无论如何这都是一个很好的做法).这里是关于这个的讨论.要删除警告,您可以简单地根据 #pragma 语句使用.

  2. 您可以创建带有私有 STL 字段的小型包装类,并将您的包装类类型的字段公开给公众,但这很可能只会将警告移到其他地方.

  3. 您可以使用 PIMPL 习语来隐藏您的私有实现类中的 STL 字段,它甚至不会从您的库中导出,也不会在库外可见.

  4. 或者您实际上可以按照 C4251 警告中的建议,以此处.简而言之,您必须在 HelloWorld 类之前插入以下代码行:

    <代码>...#include <向量>模板类 __declspec(dllexport) std::allocator;模板类 __declspec(dllexport) std::vector;模板类 __declspec(dllexport) std::string;类 __declspec(dllexport) HelloWorld...

    顺便说一下,这些导出的顺序似乎很重要:向量类模板在内部使用分配器类模板,因此必须在向量实例化之前导出分配器实例化.p>

  5. 直接使用内部函数可能是您最糟糕的选择,但如果您封装字段

    int *p_abc;国际 abc_len;

    在类似class MyFancyDataArray 和字段

    char *p_str;int str_len;

    在类似 class MyFancyString 的东西中,那么这将是一个更体面的解决方案(类似于第二点描述的解决方案).但过于频繁地重新发明轮子可能不是最好的习惯.

It is not a good practice using stl-classes in the dll-interface as Common practice in dealing with warning c4251: class … needs to have dll-interface explains. An example is given:

#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;

};

When compiling this file, the following warnings can be observed:

 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'

Then the question is how we can implement the same functionality without using STL class vector and string. One implementation I could think of is as follows:

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;



};

As you can see, in the new implementation we use int *p_abc to substitute vector abc and char *p_str to replace string str. The question I have is whether there are other elegant implementation approaches that can do the same. Thanks!

解决方案

I think you've got at least 5 possible options to solve this problem:

  1. You could still keep STL/template classes for your fields and also use them as parameter types, as long as you keep all the fields private (which is a good practice anyway). Here is a discussion about this. To remove the warnings you could simply use according #pragma statements.

  2. You could create small wrapper classes with private STL fields and expose fields of your wrapper class types to the public instead, but this would most probably only move the warnings to another places.

  3. You could use the PIMPL idiom to hide your STL fields in a private implementation class, which will not even be exported from your library nor be visible outside of it.

  4. Or you could actually export all the required template class specializations, as suggested in the C4251 warning, in a way that is described here. In short you would have to insert following lines of code before your HelloWorld class:

    ...
    #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
    ...
    

    By the way, the order of those exports seems to be important: the vector class template uses the allocator class template internally, so the allocator instantiation has to be exported before the vector instantiation.

  5. The direct use of intrinsics is probably your worst option, but if you encapsulate your fields

    int *p_abc;
    int abc_len;
    

    in something like class MyFancyDataArray and the fields

    char *p_str;
    int str_len;
    

    in something like class MyFancyString, then this would be a more decent solution (similar to the one described at the second point). But it is probably not the best habit to reinvent the wheel too often.

相关文章