zoukankan      html  css  js  c++  java
  • 不要在公共接口中传递STL容器

    最近的一个项目,是开发一个framework,提供给公司内部不同的产品线使用。 之间遇到的一个问题,就是STL容器的使用, 而结论是不要在公共接口中传递STL容器:

    这里说的STL容器,但主要则是指容器,字符串类,但其实可以推广到在STL中提供的任何类型,
    这里说的公共接口,是指需要暴露给客户的sdk头文件,包括函数签名,或者类成员变量;
    也可以说,不要在暴露给客户的头文件中包含STL的头文件。

    原因分析
    为什么有这个结论,我们可以从几个方面来论述:

    客户端使用的STL版本可能不同
    因为STL作为标准库,Framework编译的时候使用的STL,与客户端编译的时候使用的STL,版本是有可能不一样的,比如Framework使用VC8编译发布,而客户代码使用VC10编译使用,那么Framework所理解的STL容器,与客户端代码所理解的STL容器,在内存布局上,数据表示上是有可能不一样的,出错也就不可避免了。

    编译选项
    即使Framework和客户端使用的是同一个版本的STL,但是如果编译选项不一样,也可能导致其不一致性,自然也会出错。

    静态变量
    即使版本一致,编译选项一致,仍然是不安全的,因为STL容器的实现中,有可能使用了静态变量,而在Framework模块和客户端模块中,虽然由于模板扩展产生的两份代码总体上是一致的,但都各自维护了一份静态变量,从而造成了内部状态的不一致性。
    客户端的STL被自定义
    但从文件的角度来看,在客户端包含的STL头文件实现,可以是完全不同于标准的 - 只要其接口和标准保持一致。 比如客户那边使用的STL在内存管理方面有特殊需求,改写了默认的allocator,那么内存必然会出问题;比如客户在实现中加入了其他成员变量,那么内存布局必然也不一致。
    虽然,微软这篇文章提到导出vector是安全的,但是根据上面的分析,这应该是不安全的做法。

    可选方案
    但事实上,你还是可能存在这种需求的 - 你需要容器类的函数参数或者返回值,你可能也需要容器类的类成员变量,那么如何解决?

    对于前者,我们可以针对具体的类型封装一个具体的容器类,内部还是可以使用STL container的;
    对于后者,我们可以用一些办法,防止STL容器出现在头文件中;

    解决的方法,可以有以下几种:

    提供自己的容器类
    这是最彻底,最直接的方法,但是提供一套与STL相当的模板容器类需要蛮大的工作量,而且明显是重复制造轮子了。 要注意,这里简单的对STL容器进行封装是行不通,因为模板不支持分离编译,这么做还是会把STL容器带入公共接口。
    PImpl
    PImpl模式当然很强大,当然可以解决这个问题,但是此处如果仅仅是为了隐藏容器类,难免杀鸡用牛刀,还引入了不必要的间接层与繁琐的脚手架代码。
    使用void*作为数据成员,而后强制转换为容器类
    不安全也太丑陋,用起来也繁琐,每次都要cast以下,除非别无他法,是在不应该考虑这个方案。
    前置声明STL容器
    这个方法在VC9中是可以工作的,但是根据C++标准: 17.4.3.1 Reserved names,这种做法是未定义的:
    “ It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces within namespace std unless otherwise specified”

    自定义结构体封装容器并前置声明
    这个方案是这样的:

    //头文件

    struct PublicClassData;

    class PublicClass{

    public:   

    PublicClass();

    private:   

    PublicClassData* m_pData;

    }

    //实现文件

    struct PublicClassData

    {   

    vector<int> _array;   

     map<string, int> _map;

    };

    PublicClass::PublicClass()

    {   

    m_pData = new PublicClassData;

    }

    当然,需要注意的是我们需要遵循:rule of three,提供析构,赋值与拷贝构造函数以管理内存。
    这个方案相对来讲比较简洁,应该说是现有方案的最有实践性的一个。
    结论
    所以,结论就是不要在DLL/SO的公共接口中使用STL容器,如果你确实需要,那么请用自定义结构体封装容器并前置声明的方式隔离STL容器!

  • 相关阅读:
    如何配置SWRLJess Tab?
    Orz游戏开发框架阅读笔记(一)
    JessDE 在 Eclipse中不能正确安装的问题
    如何在Protege3.4中安装graphviz以便在protege中使用OwlvizTab?
    语义网的学习资源大汇集(转载)
    如何使用Eclipse从Subversion源码服务器下载源码?
    UltraEdit不能对Matlab的M文件进行语法高亮显示问题的解决
    UltraEdit的语法高亮文件网址
    IronPython的致命弱点
    【WPF】用CustomControl打造WPF版的Marquee
  • 原文地址:https://www.cnblogs.com/lidabo/p/3300412.html
Copyright © 2011-2022 走看看