代码之家  ›  专栏  ›  技术社区  ›  Anton Savelyev

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

  •  0
  • Anton Savelyev  · 技术社区  · 7 年前

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

    一旦我从NDEBUG切换到\u DEBUG,我就会得到一个“\u Block\u Type\u Is\u 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类,这会导致断言失败。我的问题是,“ 我在创建和删除类指针时是否做错了什么? 该断言在类A的析构函数的“delete p\u B”指令中特别失败。

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

    编辑2: 我为A和B类定义了复制构造函数和复制赋值操作符。它们从未被调用。

    EDIT3:值得一提的是,如果我删除 delete p_B; 语句,则不再出现断言失败。

    EDIT4:在定义了/MDd和\u Debug的调试模式下,程序运行正常。当我使用/MD和\u DEBUG在发布模式下运行时,断言失败。我认为这可能会导致问题,因为/MD可能应该与NDEBUG一起运行。

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

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

    2 回复  |  直到 7 年前
        1
  •  1
  •   Christophe    7 年前

    没有复制构造函数和赋值运算符, p_B 从其原始A对象克隆。因此,销毁的两个对象中的第一个将删除 p\u B ,第二个对象将尝试删除已删除的指针,即UB。

    在编辑中,定义了缺少的元素。复制构造函数的问题在于它什么都不做。不幸的是 p\u B 复制对象的指针可能无效。您需要完成这些成员功能:

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

    对于复制构造函数,假设您保证p\u 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函数中实例化。但对于您的很短的代码片段,它似乎是此函数的本地对象。所以一旦你离开这个函数,它就会被破坏。

        2
  •  1
  •   Anton Savelyev    7 年前

    我确信这个问题不是源于代码,而是源于项目配置属性。当/MD与\u DEBUG配对时,似乎会出现此错误。然而,当一个项目被纳入发布版本时,不应该定义调试,而应该定义NDEBUG。一旦我将预处理器定义从\u DEBUG更改为NDEBUG,断言失败就不再出现了。

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