当前位置:  开发笔记 > 编程语言 > 正文

内联类构造函数,以避免vc内存崩溃

如何解决《内联类构造函数,以避免vc内存崩溃》经验,为你挑选了1个好方法。

C++类构造函数可以内联或不内联.但是,我发现了一个奇怪的情况,只有内联类构造函数可以避免Visual Studio内存崩溃.示例如下:

dll.h

class _declspec(dllexport) Image 
{
    public:
        Image();
        virtual ~Image();
};

class _declspec(dllexport) Testimage:public Image 
{
public:
    Testimage();

    virtual ~Testimage();
};

typedef std::auto_ptr TestimagePtr;

dll.cpp

#include "dll.h"
#include 


Image::~Image()
{
            std::cout<<"Image is being deleted."<

dll库被编译为动态库,并且它静态链接到C++运行时库(Multi-threaded Debug (/MTd)).运行库的可执行程序如下:

int main()
{
    TestimagePtr my_img(new Testimage());
    return 0;
}

可执行程序将调用dll库,它还静态链接运行时库.我遇到的问题是,在运行可执行程序时,会出现以下错误消息: 在此输入图像描述

但是,当dll中的类构造函数被内联时,以下代码显示:

class _declspec(dllexport) Image 
{
    public:
        Image();
        virtual ~Image();
};

class _declspec(dllexport) Testimage:public Image 
{
public:
    Testimage()
    {
    }

    virtual ~Testimage();
};

崩溃将消失.有人可以解释背后的原因吗?谢谢!顺便说一下,我正在使用VC2010.

编辑:以下情况也会触发相同的崩溃.

情况1

int main()
{
    //TestimagePtr my_img(new Testimage());
    Testimage *p_img;
    p_img = new Testimage();
    delete p_img;
    return 0;
}

Hans Passant.. 13

它静态链接到C++运行时库(多线程调试(/ MTd))

在VS2012之前的Visual Studio版本中,这是一个非常有问题的场景.问题是您的流程中加载了多个版本的CRT.一个由EXE使用,另一个由DLL使用.这可能会导致许多微妙的问题,而不是像这次崩溃那样微妙的问题.

CRT具有全局状态,当该全局状态由CRT的一个副本更新并由另一个副本读回时,诸如errno和strtok()之类的东西无法正常工作.与崩溃相关的是,隐藏的全局状态变量是CRT用于分配内存的堆.像malloc()和:: operator new这样的函数使用该堆.

当对象由CRT的一个副本分配并由另一个副本释放时,这会出错.传递给free()或:: operator delete的指针属于错误的堆.接下来会发生什么取决于您的操作系统.XP中的静默内存泄漏.在Vista及更高版本中,程序运行时启用了内存管理器的调试版本.当您的进程附加调试器时,会触发断点,以告诉您指针存在问题.屏幕截图中的对话框就是结果.对于我来说,内联构造函数如何能够产生影响并不是很清楚,但基本问题是你的代码调用了未定义的行为.它具有产生随机结果的诀窍.

有两种方法可以解决这个问题.第一个是简单的,只需使用/ MD编译选项构建您的EXE和DLL项目.这将选择CRT的DLL版本.它现在由两个模块共享,并且您的流程中只有一个CRT副本.因此,一个模块分配和另一个模块释放内存不再有问题,使用相同的堆.

这可以很好地解决您的问题,但以后仍然可能成为一个问题.DLL往往过着自己的生活,有一天可能被另一个用不同版本的CRT构建的EXE 使用.CRT现在将再次不被共享,因为他们将使用不同版本的DLL,调用您今天看到的完全相同的故障模式.

保证不会发生这种情况的唯一方法是仔细设计DLL接口.并确保永远不会出现DLL分配客户端代码需要释放的内存的情况.这需要放弃很多C++好东西.例如,你永远不能编写一个返回C++对象的函数,比如std :: string.并且您永远不能允许异常跨越模块边界.你基本上是C风格的界面.请注意COM如何通过使用基于接口的编程技术和类工厂以及引用计数来解决内存管理问题来解决此问题.

VS2012有针对此问题的对策,它有一个从默认进程堆分配的CRT版本.这解决了这个特殊问题,而不是其他运行时函数的全局状态问题的解决方法.并添加了一些新问题,一个用/ MT编译的DLL被卸载但不会释放所有的分配现在会导致不可插拔的泄漏.

这是C++中一个丑陋的问题,该语言从根本上错过了解决此类问题的ABI规范.语言规范完全没有模块的概念.今天工作但尚未完成.这并不简单,它通过指定虚拟机在Java和.NET语言等其他语言中得到解决,提供了一个集中内存管理的运行时环境.不是那种激发C++程序员兴奋的运行时环境.



1> Hans Passant..:

它静态链接到C++运行时库(多线程调试(/ MTd))

在VS2012之前的Visual Studio版本中,这是一个非常有问题的场景.问题是您的流程中加载了多个版本的CRT.一个由EXE使用,另一个由DLL使用.这可能会导致许多微妙的问题,而不是像这次崩溃那样微妙的问题.

CRT具有全局状态,当该全局状态由CRT的一个副本更新并由另一个副本读回时,诸如errno和strtok()之类的东西无法正常工作.与崩溃相关的是,隐藏的全局状态变量是CRT用于分配内存的堆.像malloc()和:: operator new这样的函数使用该堆.

当对象由CRT的一个副本分配并由另一个副本释放时,这会出错.传递给free()或:: operator delete的指针属于错误的堆.接下来会发生什么取决于您的操作系统.XP中的静默内存泄漏.在Vista及更高版本中,程序运行时启用了内存管理器的调试版本.当您的进程附加调试器时,会触发断点,以告诉您指针存在问题.屏幕截图中的对话框就是结果.对于我来说,内联构造函数如何能够产生影响并不是很清楚,但基本问题是你的代码调用了未定义的行为.它具有产生随机结果的诀窍.

有两种方法可以解决这个问题.第一个是简单的,只需使用/ MD编译选项构建您的EXE和DLL项目.这将选择CRT的DLL版本.它现在由两个模块共享,并且您的流程中只有一个CRT副本.因此,一个模块分配和另一个模块释放内存不再有问题,使用相同的堆.

这可以很好地解决您的问题,但以后仍然可能成为一个问题.DLL往往过着自己的生活,有一天可能被另一个用不同版本的CRT构建的EXE 使用.CRT现在将再次不被共享,因为他们将使用不同版本的DLL,调用您今天看到的完全相同的故障模式.

保证不会发生这种情况的唯一方法是仔细设计DLL接口.并确保永远不会出现DLL分配客户端代码需要释放的内存的情况.这需要放弃很多C++好东西.例如,你永远不能编写一个返回C++对象的函数,比如std :: string.并且您永远不能允许异常跨越模块边界.你基本上是C风格的界面.请注意COM如何通过使用基于接口的编程技术和类工厂以及引用计数来解决内存管理问题来解决此问题.

VS2012有针对此问题的对策,它有一个从默认进程堆分配的CRT版本.这解决了这个特殊问题,而不是其他运行时函数的全局状态问题的解决方法.并添加了一些新问题,一个用/ MT编译的DLL被卸载但不会释放所有的分配现在会导致不可插拔的泄漏.

这是C++中一个丑陋的问题,该语言从根本上错过了解决此类问题的ABI规范.语言规范完全没有模块的概念.今天工作但尚未完成.这并不简单,它通过指定虚拟机在Java和.NET语言等其他语言中得到解决,提供了一个集中内存管理的运行时环境.不是那种激发C++程序员兴奋的运行时环境.

推荐阅读
mobiledu2402852413
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有