在发布版本中删除 class 指针会导致内存问题
Deletion of a class pointer in release build causes memory issues
使用VS2010编写的/clr的MFC应用程序。多线程 DLL (/MD) 运行-时间库。当我将 NDEBUG 的预处理器定义切换为 _DEBUG 时出现问题。 NDEBUG 禁用定义 _DEBUG 时弹出的断言。我在管理 class 指针的创建和删除时做错了什么吗?
从 NDEBUG 切换到 _DEBUG 后,我在 运行 处收到“_Block_Type_Is_Valid (pHead->nBlockUse)”断言失败 错误时间。
Class答:"A.h"
#include "B.h"
class A
{
public:
A(void);
~A(void);
A(const A&);
A& operator=(const A&);
B* p_B;
};
Class答:"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;
}
Class乙:"B.h"
class B
{
public:
B(void);
~B(void);
B(const B&);
B& operator=(const B&);
};
Class乙:"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(实例化classA的一个对象)
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 class,这导致断言失败。我的问题是,“我在创建和删除 class 指针时做错了吗?”断言在 "delete p_B" 指令中特别失败 class A 的析构函数。
编辑:
我用 BOOL CMyMFCClassDLG::OnInitDialog() { ... A a; ...}
实例化 A class
编辑2:
我为 A 和 B classes 定义了复制构造函数和复制赋值运算符。他们永远不会被调用。
EDIT3:值得一提的是,如果我删除 A 的析构函数中的 delete p_B;
语句,则不再有断言失败。
EDIT4:程序 运行 在定义了 /MDd 和 _DEBUG 的调试模式下运行良好。当我 运行 在发布模式下使用 /MD 和 _DEBUG 时断言失败。我认为这可能是导致问题的原因,因为 /MD 可能应该是 运行 with NDEBUG.
EDIT5:我按照@Christophe 的建议更新了代码,并插入了实例化 Class A 的对象的函数。我不想 copy/paste 模态对话框应用程序的其余部分,但您可以通过在 VS2010 中启动一个新的基于模态对话框的 MFC 应用程序并更改项目配置以使用 /CLR 模式来复制确切的代码,设置 运行-Time Library to /MD 并在预处理器定义字段中包含 _DEBUG 关键字。
EDIT6:Link 到项目 https://drive.google.com/drive/folders/1q0n9c6yMZ2ZKnakH6Z5NbVeGsAWfUAc1?usp=sharing
没有复制构造函数和赋值运算符,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;
如果您的代码不小心使用了它们,编译器会抱怨而不是生成代码和上述问题。
最后,您在初始化函数中实例化 A。但是对于你的非常短的片段,它似乎是这个函数的本地对象。因此,一旦您离开该功能,它就会被销毁。
我确定问题不是源于代码,而是源于项目配置属性。当 /MD 与 _DEBUG 配对时,似乎会出现此错误。但是,当一个项目进入发布构建时,不应定义 _DEBUG,而应该定义 NDEBUG。一旦我将预处理器定义从 _DEBUG 更改为 NDEBUG,断言失败就不会再出现了。
值得注意的是,使用 /MDd 和 _DEBUG 时项目执行没有问题。
使用VS2010编写的/clr的MFC应用程序。多线程 DLL (/MD) 运行-时间库。当我将 NDEBUG 的预处理器定义切换为 _DEBUG 时出现问题。 NDEBUG 禁用定义 _DEBUG 时弹出的断言。我在管理 class 指针的创建和删除时做错了什么吗?
从 NDEBUG 切换到 _DEBUG 后,我在 运行 处收到“_Block_Type_Is_Valid (pHead->nBlockUse)”断言失败 错误时间。
Class答:"A.h"
#include "B.h"
class A
{
public:
A(void);
~A(void);
A(const A&);
A& operator=(const A&);
B* p_B;
};
Class答:"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;
}
Class乙:"B.h"
class B
{
public:
B(void);
~B(void);
B(const B&);
B& operator=(const B&);
};
Class乙:"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(实例化classA的一个对象)
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 class,这导致断言失败。我的问题是,“我在创建和删除 class 指针时做错了吗?”断言在 "delete p_B" 指令中特别失败 class A 的析构函数。
编辑:
我用 BOOL CMyMFCClassDLG::OnInitDialog() { ... A a; ...}
编辑2: 我为 A 和 B classes 定义了复制构造函数和复制赋值运算符。他们永远不会被调用。
EDIT3:值得一提的是,如果我删除 A 的析构函数中的 delete p_B;
语句,则不再有断言失败。
EDIT4:程序 运行 在定义了 /MDd 和 _DEBUG 的调试模式下运行良好。当我 运行 在发布模式下使用 /MD 和 _DEBUG 时断言失败。我认为这可能是导致问题的原因,因为 /MD 可能应该是 运行 with NDEBUG.
EDIT5:我按照@Christophe 的建议更新了代码,并插入了实例化 Class A 的对象的函数。我不想 copy/paste 模态对话框应用程序的其余部分,但您可以通过在 VS2010 中启动一个新的基于模态对话框的 MFC 应用程序并更改项目配置以使用 /CLR 模式来复制确切的代码,设置 运行-Time Library to /MD 并在预处理器定义字段中包含 _DEBUG 关键字。
EDIT6:Link 到项目 https://drive.google.com/drive/folders/1q0n9c6yMZ2ZKnakH6Z5NbVeGsAWfUAc1?usp=sharing
没有复制构造函数和赋值运算符,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;
如果您的代码不小心使用了它们,编译器会抱怨而不是生成代码和上述问题。
最后,您在初始化函数中实例化 A。但是对于你的非常短的片段,它似乎是这个函数的本地对象。因此,一旦您离开该功能,它就会被销毁。
我确定问题不是源于代码,而是源于项目配置属性。当 /MD 与 _DEBUG 配对时,似乎会出现此错误。但是,当一个项目进入发布构建时,不应定义 _DEBUG,而应该定义 NDEBUG。一旦我将预处理器定义从 _DEBUG 更改为 NDEBUG,断言失败就不会再出现了。
值得注意的是,使用 /MDd 和 _DEBUG 时项目执行没有问题。