4

我在 Visual Studio 2010 C++ 编译器中遇到了奇怪的行为。以下代码编译但在执行后抛出“调试断言失败”并显示消息:

“_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)”

在 GCC 下编译运行流畅。是我的错吗?

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int*> coll;
public:

    void add(int* item) {
       coll.push_back(item);
    }

    ~Foo() {
        for (uint i = 0; i < coll.size(); ++i) {
            delete coll[i];
            coll[i] = NULL;
        }
    }
};

int main()
{
   Foo foo;
   foo.add(new int(4));
   Foo bar = foo;

   return 0;
}
4

4 回答 4

7

您没有实现复制构造函数和复制赋值运算符(参见规则三)。这会导致向量中指针的浅拷贝,从而导致双重删除和断言。编辑:双重删除是未定义的行为,因此 VS 和 gcc 在这里都是正确的,他们可以做任何他们想做的事情。

通常,当您实现具有非平凡行为的析构函数时,您还需要编写或禁用复制构造和复制分配。

但是,在您的情况下,您真的需要通过指针存储项目吗?如果没有,只需按值存储它们即可解决问题。否则,如果您确实需要使用指针shared_ptr(来自您的编译器或 boost)而不是原始指针,以使您无需编写自己的析构函数/复制方法。

编辑:关于您的接口的进一步说明:像这样的接口传递传入指针的所有权可能会导致使用您的类的人感到困惑。如果有人传入了未在堆上分配的 int 的地址,那么您的析构函数仍然会失败。最好是尽可能按值接受,或者克隆传入的项目,newadd函数中自己调用。

于 2011-05-06T15:54:27.577 回答
3

您要删除项目两次,因为该行

Foo bar = foo;

调用默认的复制构造函数,它复制 itempointer,而不是分配和复制数据。

于 2011-05-06T15:54:26.267 回答
2

问题是barfoo成员的向量元素是相同的。当foo超出范围时,它的析构函数被调用,它释放指针,使bar向量元素悬空。bar析构函数尝试释放它的向量元素,该元素悬空并导致运行时错误。您应该编写一个复制构造函数。

Foo bar = foo; // Invokes default copy constructor.

编辑 1:查看此线程以了解规则三

于 2011-05-06T15:54:44.143 回答
0

这里更简单的解决方案是int*首先不要使用。

#include <iostream>
#include <vector>


using namespace std;

typedef unsigned int uint;


class Foo {
    vector<int> coll; // remove *
public:

    void add(int item) { // remove *
       coll.push_back(item);
    }

    // remove ~Foo
};

int main()
{
   Foo foo;
   foo.add(4); // remove `new` call
   Foo bar = foo;

   return 0;
}

一般来说,尽量避免new

如果不能,请使用智能管理器(如std::unique_ptr)为您处理内存清理。

无论如何,如果您delete手动调用,那么您做错了注意:不调用delete并让内存泄漏也是错误的

于 2011-05-06T16:34:58.557 回答