zoukankan      html  css  js  c++  java
  • C++ string warning C4251

    有一次在项目中用到C++的string (以前一直是用C的,对于C++的一些特性不是很了解),记录下遇到的一些问题。

    整个项目有一个DLL和一个exe程序,DLL的类成员里面使用的一些string(主要是用它的find 、+的功能),在编译的时候有warning C4251的警告,F5运行程序没有什么问题,但是直接打开exe的时候就崩溃了,怀疑和这个C4251有关,在网上查看了一下发现string类并不是一个DLL的导出类,程序在执行的时候,可能会调用不动的DLL库,有些说的添加template  class __declspec( dllexport ) std::***没有用,警告依然在,程序直接运行,也还是会崩溃。

    最后找到了一个说的比较深入,也比较有用的方法。

    这是因为STL里的string类并非是一个DLL的输出类,这会导致类的成员变量name无法正确输出。
    在你将CTest定义为一个导出类后,CTest中的所有成员函数都将编译连接为DLL的输出函数。包括一些隐含的构造/析构函数以及operator=等。如果一个类的一些PUBLIC成员变量不是输出类,那么DLL的使用者对这些公共成员的方法的访问可能会导致访问不同的VC运行库。

    分析下面的代码:

    CTest  test;  
    // 调用CTest的构造函数,由于CTest是输出类,构造函数也是DLL的输出函数,构造函数调用的string的构造函数,string调用VC运行库分配内存,这个时候string类使用的运行库是与CTest实现所在的DLL进行连接的那个VC运行库

    test.intValue  =  3;           //没有问题  
    // 没有问题,因为对整数的赋值与运行库无关

    test.name  =  "name";          //编译、连接都通过,运行时出现内存访问异常:  
                                   //test.name中的数据指针是非法的堆指针
      
    // 由于string不是DLL的输出类,所以string的operator=不是一个DLL内部的函数,
    对string的operator=成员的访问会导致编译器调用当前的模块(EXE)使用的VC运行库中的函数,这便产生了问题。



    // 同样在析构的时候也可能产生这样的问题

    解决的办法是
    在编译两个模块时选择相同类型的DLL版本的VC运行库,相同类型的静态的VC运行库不解决问题,因为在这种情况下,不同的模块分别包含VC静态运行库的静态数据和代码。

    其实问题很简单,为了便于说明问题,假定CTest实现所在的DLL是TEST.DLL, CTest的使用者是CLIENT.EXE。
    在构造CTest的时候,CTest的构造函数调用string的构造函数,这个时候string使用的是与TEST.DLL连接的运行库中的内存分配函数。
    当CLIENT.EXE代码中对string赋值的时候,会导致string重新分配内存,这个时候string使用的是与CLIENT.EXE连接的运行库中的内存分配函数。在这个过程中,string会将在构造时分配的内存释放掉,这些内存是由与TEST.DLL连接的运行库的内存分配函数分配的,但是却由与CLIENT.EXE连接的运行库的内存分配函数释放,如果TEST.DLL和Client.EXE使用的是同样类型的运行库DLL(都使用Debug Multi-thread DLL, 或者Release Multi-Thread DLL), 那么是不会有问题的,因为这个时候VC的运行库中的malloc, free使用同一的CRT内部数据。

    如果使用不同的CRT库,那么实际上在CLIENT.EXE运行的时候,进程的地址空间里实际上存在两份不同的CRT的代码和数据(分别被TEST.DLL和CLIENT.EXE使用), 当一个CRT的free函数企图释放由另一个CRT通过malloc分配的函数的时候,就会出现问题,这个时候CrtIsValidHeapPointer函数就会认为这不是一个有效的堆指针。

    那么如果使用同样的静态库呢?这个问题同样存在,因为如果使用静态库,在CLIENT.EXE运行的时候,进程的地址空间里还是会存在两份CRT的代码和数据,尽管他们的代码和数据结构很多都一样。

    类似的问题有通过一个CRT的fopen获得一个FILE指针,然后由另一个CRT的fclose关闭等等。




    否则,在释放内存的时候,那么CrtIsValidHeapPointer就会告诉你这快内存不是由这个CRT分配的。

    转载注明出处http://www.cnblogs.com/WJIUYANG/p/3509316.html

  • 相关阅读:
    sqlServer的主键只能自增不能手动增加
    TP函数
    TP复习17
    TP复习16
    TP复习15
    TP复习14
    TP复习13
    TP复习12
    TP复习11
    TP复习10
  • 原文地址:https://www.cnblogs.com/WJIUYANG/p/3509316.html
Copyright © 2011-2022 走看看