在发布版本中删除类指针会导致内存问题

时间:2018-01-16 16:39:43

标签: c++ mfc clr

使用VS2010编写的/ clr的MFC应用程序。多线程DLL(/ MD)运行时库。当我将 NDEBUG的预处理器定义切换为_DEBUG 时出现问题。 NDEBUG禁用在定义_DEBUG时弹出的断言。我是否在管理类指针的创建和删除方面做错了什么?

一旦我从NDEBUG切换到_DEBUG,我得到一个" _Block_Type_Is_Valid(pHead-> nBlockUse)"运行时断言失败错误。

A类:" A.h"

#include "B.h"

class A
{
public:
    A(void);
    ~A(void);

    A(const A&);
    A& operator=(const A&);

    B* p_B;

};

A类:" A.cpp"

#include "StdAfx.h"
#include "A.h"

A::A(void)
{
    p_B = new B();
}

A::~A(void)
{
    delete p_B;
}

// 1. copy constructor
A::A(const A& that)
{
     p_B = new B(); 
    *p_B = *that.p_B;
}

// 2. copy assignment operator
A& A::operator=(const A& that)
{
    *p_B = *that.p_B;
    return *this;
}

B组:" B.h"

class B
{
public:
    B(void);
    ~B(void);

    B(const B&);
    B& operator=(const B&);
};

B组:" B.cpp"

#include "StdAfx.h"
#include "B.h"

B::B(void) { }
B::~B(void) { }

// 1. copy constructor
B::B(const B& that)
{
}

// 2. copy assignment operator
B& B::operator=(const B& that)
{
    return *this;
}

ModalDlg.cpp(其中实例化了A类的对象)

BOOL CTestingReleaseBuildDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    A a;

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: Add extra initialization here

    return TRUE;  // return TRUE  unless you set the focus to a control
}

然后我只是在我的MFC对话框中实例化A类,这会导致断言失败。我的问题是," 我在创建和删除类指针时是否做错了什么?"断言在"删除p_B"中特别失败。 A级解析器的指导。

编辑: 我使用BOOL CMyMFCClassDLG::OnInitDialog() { ... A a; ...}

实例化A类

EDIT2: 我为A和B类定义了复制构造函数和复制赋值运算符。永远不会调用它们。

EDIT3:值得一提的是,如果我删除A'析构函数中的delete p_B;语句,则不再有断言失败。

EDIT4:在调试模式下,程序运行正常,并定义了/ MDd和_DEBUG。使用/ MD和_DEBUG在Release模式下运行时,断言失败。我认为可能导致这个问题,因为/ MD应该与NDEBUG一起运行。

EDIT5:我更新了@Christophe建议的代码,并插入了实例化A类对象的函数。我不想复制/粘贴Modal Dialog应用程序的其余部分,但您可以通过在VS2010中启动基于Modal Dialog的新MFC应用程序来复制确切的代码,并将项目配置更改为使用/ CLR模式,设置运行时库到/ MD并在预处理器定义字段中包含_DEBUG关键字。

EDIT6:链接到项目https://drive.google.com/drive/folders/1q0n9c6yMZ2ZKnakH6Z5NbVeGsAWfUAc1?usp=sharing

2 个答案:

答案 0 :(得分:1)

如果没有复制构造函数和赋值运算符,p_B将从其原始A对象进行克隆。因此,被破坏的两个对象中的第一个将删除p_B,第二个对象将尝试删除已删除的指针,即UB。

在编辑中,您已定义缺少的元素。您的复制构造函数的问题,它什么都不做。遗憾的是,复制对象的p_B指针可能无效。您需要完成这些成员函数:

// 1. copy constructor
A::A(const A& that)
{
    p_B = new B(); 
    *p_B = *that->p_B;
}

对于复制构造函数,假设您保证p_B将始终指向有效的B对象,并假设不存在切片风险:

// 2. copy assignment operator
A& A::operator=(const A& that)
{
    *p_B = *that.p_B;        
    return *this;
}

如果您认为不需要复制构造函数或赋值运算符,为了确保遵守3的规则,您也可以将它们声明为已删除:

A(const A&) = delete;
A& operator=(const A&) = delete;

如果您的代码意外使用它们,编译器会抱怨而不是生成代码和上面提到的问题。

最后,在init函数中实例化A。但是,使用非常短的代码片段,它似乎是此功能的本地对象。一旦你离开那个功能就会被摧毁。

答案 1 :(得分:1)

我确定问题不是来自代码,而是来自项目配置属性。当/ MD与_DEBUG配对时,似乎会出现此错误。但是,当项目进入Release Build时,不应定义_DEBUG,而应定义NDEBUG。一旦我将预处理器定义从_DEBUG更改为NDEBUG,断言失败就不再出现了。

值得注意的是,在使用/ MDd和_DEBUG时,项目执行时没有问题。