C++ 使用管理器和向量时出现内存错误

C++ 使用管理器和向量时出现内存错误,c++,memory,vector,C++,Memory,Vector,我想创建一个包含多个对象的管理器,并且必须使用它才能真正创建对象。 对象将其信息保存在智能指针中。 我就是这样实现的: struct Object { std::shared_ptr<int> number; }; struct Manager { std::vector<Object> objects; Object& createObject() { objects.emplace_back();

我想创建一个包含多个对象的管理器,并且必须使用它才能真正创建对象。 对象将其信息保存在智能指针中。 我就是这样实现的:

struct Object
{
    std::shared_ptr<int> number;
};

struct Manager
{
    std::vector<Object> objects;
    Object& createObject()
    {
        objects.emplace_back();
        return objects.back();
    }
};

int main()
{
    Manager manager;
    Object& object1 = manager.createObject();

    object1.number = std::make_shared<int>(10);

    for (Object& o : manager.objects)
    {
        std::cout << *o.number << std::endl;
    }
}

有人知道为什么会发生这种情况吗?

向向量添加新元素时,对旧对象的引用可能会失效

看看上面写着什么:

如果新的
size()
大于
capacity()
,则所有迭代器和引用(包括过去的结束迭代器)都将无效


因此,在添加
object2
之后,引用
object1
可能无效。

将类实例的向量与指向这些类实例的指针或引用结合使用从来都不是一个好主意。正如Bo Persson已经正确回答的那样,由于
std::vector
的动态特性,这些指针或引用往往会变得悬空:当
std::vector
增长时,它通常会将其项复制到不同的内存位置,使已经存在的项引用和指针无效(悬空)

通过存储指向类的指针而不是类本身,可以很容易地避免这种情况

struct Manager
{
    std::vector<std::unique_ptr<Object>> objects;
    Object& createObject()
    {
        objects.emplace_back(std::make_unique<Object>());
        return *objects.back().get();
    }
};
结构管理器 { 向量对象; 对象&createObject() { objects.emplace_back(std::make_unique()); return*objects.back().get(); } }; 现在,
std::vector
可以随心所欲地移动
唯一的\u ptr
——智能指针内容(原始指针),因此引用也不会更改(当然,除非您有意更改或删除它们)

下面是使用类实例向量时发生的情况。 灰色的垂直条纹象征着记忆——这里忽略了记忆的真实结构和大小

步骤1:有一个向量(用方括号表示)将类实例作为一个项保存。矢量后面的内存被占用(现实有点不同,但图像应该足够)

步骤2:创建指向类实例的引用(或指针)。(绿色箭头)

步骤3:向向量添加第二个类实例。向量没有空间容纳其项,因此必须将其内容移动到另一个内存位置。您的指针/引用已断开!(红色箭头)

下面是指针解决方案的示例:

步骤1:你又有了一个向量,但现在它是一个智能指针向量。它拥有一个指向类实例的智能指针(深绿色箭头)

步骤2:再次创建指向类实例的引用(或指针)。(绿色箭头)

步骤3:向向量添加指向类实例的第二个指针。向量没有空间容纳其项,因此必须将其内容移动到另一个内存位置。但是这次只移动智能指针,而不是类实例本身!类实例1保持不变,智能指针1仍然指向类实例1,您的引用保持不变,每个人都很高兴:)

此外:除了是一个安全的解决方案外,使用指针向量而不是实例向量通常也有性能优势
unique_ptr
非常小,几乎总是比它们持有指针的对象小得多。因此,当
std::vector
必须将其项复制到不同的内存位置时,如果这些只是小的智能指针,那么它所要做的工作就会少很多。
除此之外,还有一些类具有昂贵的复制构造函数(例如锁定!)。如果根本不复制类实例,所有这些都可以避免。

您遇到了什么错误?我的错误代码不是英文的,但类似于在0xDDDDE1位置写入时的访问冲突。是的,您不能引用局部变量并在其作用域之外使用它。我必须使用管理器正确更改对象而不是使用引用?@nullqube它是结构的局部变量而不是函数的局部变量它可以被引用谢谢,通过使用vector::reserve我可以增加容量,它修复了我的错误。哦,不。。。那肯定不能纠正你的错误,多尔福。。。它只是将其推迟到稍后的时间-当向量再次扩展时;)请查看我的答案以了解真正的解决方案。@user2328447您是对的,谢谢,我不完全理解为什么它与您的解决方案一起工作,我想我有一些家庭作业要做;)也许我可以把答案扩大一点,这样原则就更清楚了。。。我会试试的。@Dolfos,我写了一篇文章解释了它是如何工作的:。我想你会从阅读这本书中受益,但至少可以看看“Bug警报”部分。我会帮助的。我仍然不清楚指针只保存指向它所指向的对象的内存地址,那么为什么在std::vector与指针指向的对象发生冲突之后,指针仍然指向正确的内存地址?还是我理解错了什么?请等等。。。我只是在做一个说明,应该可以澄清这一点。哦,哇,我忘了只有指针在移动,而不是对象本身。现在它有意义了。非常感谢你花时间给我解释。
void _Decref()
        {   // decrement use count
        if (_MT_DECR(_Uses) == 0)
            {   // destroy managed resource, decrement weak reference count
            _Destroy();
            _Decwref();
            }
        }
struct Manager
{
    std::vector<std::unique_ptr<Object>> objects;
    Object& createObject()
    {
        objects.emplace_back(std::make_unique<Object>());
        return *objects.back().get();
    }
};