4
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cassert>

struct s_A {
    bool bin;
    s_A(): bin(0) {}
};

class c_A {
public:
    s_A * p_struct;

    c_A(): p_struct(NULL) {p_struct = new s_A [16];}

    void Reset()
    {
        delete [] p_struct;
        p_struct = new s_A [16];
    }
};

int main () 
{   
    srand(1);
    int x = 30;
    std::vector <c_A> objects;
    objects.assign(x, c_A());
    std::vector <c_A> objects_copy;

    for(int q=0; q < x; q++)
    {
        objects_copy.push_back(objects[ rand() % x ]);
        objects_copy[q].Reset();
    }

    for(int q=0; q < 16; q++)
        for(int w=0; w < x; w++)
        {
            // Assertion should not fail, but it does
            assert(!objects_copy[w].p_struct[q].bin);
            objects_copy[w].p_struct[q].bin = true;
        }
}

不知何故,不同复制对象中的指针最终指向相同的内存,断言最终失败。如果在未复制的向量上运行,则不会发生此行为。我认为 c_A.Reset() 应该释放指针(通过 delete[])以指向新数组,但我显然遗漏了一些东西。

4

1 回答 1

5

您的问题的特定来源是这里的这些行:

objects_copy.push_back(objects[ rand() % x ]);
objects_copy[q].Reset();

objects_copy问题是,当您尝试将对象的副本推送到objects vector. 这意味着两个向量中的对象最终将具有相互复制的指针。因此,当您Reset在 的元素上调用时objects_copy vector,您将释放仍然由objects数组元素指向的内存。

问题是你的c_A班级违反了三的规则。因为你的类封装了一个资源,所以它需要有析构函数、复制构造函数和复制赋值运算符。如果您定义了这三个函数,那么当您尝试将对象复制到 中时objects_copy vector,您将能够管理底层资源,可能是通过复制或引用计数。有关如何编写这些函数的详细信息,请查看如何编写这些函数的说明

编辑:这是对正在发生的事情的更详细描述:

问题是,当您将对象添加到 avector时,您实际上并没有将该对象存储在vector. 相反,您正在存储该对象的副本。因此,当您编写时objects_copy.push_back(objects[ rand() % x ]);,您不会在两者中存储相同的对象vectors。相反,您正在创建其中一个对象的副本objects并将其存储在objects_copy. 由于您的c_A类型没有定义复制函数,因此最终会创建对象的浅拷贝,从而创建指针的副本。这意味着如果您考虑objects列表中的原始对象及其对应的副本objects_copy,它们将各自拥有相同p_struct指针的副本。当你调用Reset对象中的objects_copy vector,你释放了它的指针指向的内存。但是,您没有更新存储在 中的原始对象的指针objects,因此该指针现在指向垃圾内存。尝试使用该指针会导致未定义的行为,从而导致崩溃。

添加复制功能将通过允许您控制复制的方式来解决此问题。如果您为此定义一个复制函数c_A,导致副本指向原始对象指向的对象的新副本,则不会发生此问题,因为每个对象都有自己的单独指针。或者,如果您使用引用计数,那么如果您知道其他一些对象指向该资源,则可以通过不删除该资源来避免该问题。

希望这可以帮助!

于 2011-06-01T22:45:02.357 回答