使用Manager和引导程序时出现内存错误 [英] Memory error while working with an Manager and an vector

查看:97
本文介绍了使用Manager和引导程序时出现内存错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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;
    }
}

如果执行此代码,我将得到预期的输出:10

但是一旦我尝试创建多个这样的对象:

Manager manager;
Object& object1 = manager.createObject();
Object& object2 = manager.createObject();

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

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

在此函数中,我在内存库中遇到运行时错误:

void _Decref()
        {   // decrement use count
        if (_MT_DECR(_Uses) == 0)
            {   // destroy managed resource, decrement weak reference count
            _Destroy();
            _Decwref();
            }
        }

有人知道为什么会这样吗?

解决方案

将类实例的向量与指向这些类实例的指针或引用结合使用永远不是一个好主意.就像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();
    }
};

现在std::vector可能会随心所欲地移动unique_ptr-智能指针的内容(原始指针),因此引用也永远不会更改(当然,除非您有意更改或删除它们)

这是一个示例,当您使用类实例的向量时会发生什么. 灰色的竖条纹表示内存-此处忽略了内存的实际结构和大小.

步骤1:您有一个向量(用方括号表示)将一个类实例作为项目保存.向量后面的内存已被占用(现实有些不同,但是图像就足够了)

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

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

这是指针解决方案的说明:

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

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

第3步:向向量添加指向类实例的第二个指针.向量没有空间容纳其项目,因此必须将其内容移动到另一个存储位置.但是这一次只移动了智能指针,而不移动了类实例本身!类实例1停留在其位置,智能指针1仍指向类实例1,您的引用保持不变,并且每个人都保持快乐:)

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

I want to create a Manager that holds multiple objects and has to be used in order to actually create the objects. The objects hold their information in a smart pointer. this is how I implemented it:

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;
    }
}

If I execute this code I get my expected output: 10

but once I try to create multiple objects like this:

Manager manager;
Object& object1 = manager.createObject();
Object& object2 = manager.createObject();

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

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

I get an runtime error in the memory library at this function:

void _Decref()
        {   // decrement use count
        if (_MT_DECR(_Uses) == 0)
            {   // destroy managed resource, decrement weak reference count
            _Destroy();
            _Decwref();
            }
        }

does anybody know why this is happening?

解决方案

It is never a good idea to use vectors of class instances in conjunction with pointers or references to these class instances. Like Bo Persson already correctly answered, these pointers or references tend to become dangling due to the dynamic nature of a std::vector: when a std::vector grows, it often copies its items to a different memory position, leaving the already existing item references and pointers invalid (dangling).

You can easily avoid that by storing pointers to classes instead of the classes itself.

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

Now std::vector may move the unique_ptr's around as it likes - the smart pointers content (raw pointers) and thus also the references never change (except if you willful change or delete them, of course)

Here's an illustration what happens when you use a vector of class instances. The grey vertical stripes symbolize the memory - the real structure of memory and sizes are ignored here.

Step 1: You have a vector (symbolized by square brackets) holding a class instance as an item. The memory behind the vector is occupied (reality is a little different, but the image should suffice)

Step 2: You create a reference (or pointer) to your class instance. (green arrow)

Step 3: You add a second class instance to the vector. The vector has no room for its items, and thus have to move its content to another memory position. Your pointer/reference is broken! (red arrow)

And here's an illustration of the pointer solution:

Step 1: You have a vector again, but now it's a vector of smart pointers. It holds a smart pointer pointing (dark green arrow) to a class instance.

Step 2: You again create a reference (or pointer) to your class instance. (green arrow)

Step 3: You add a second pointer to a class instance to the vector. The vector has no room for its items, and thus has to move its content to another memory position. But this time only the smart pointers are moved, not the class instances itself! Class instance 1 stays at its place, smart pointer 1 still points to class instance 1, your reference stays intact, and everyone stays happy :)

Additionally: Apart from being a safe solution, using pointer vectors instead of instance vectors very often also has a performance benefit. unique_ptr are very small, nearly always much smaller than the objects they hold pointers to. And so, when std::vector has to copy its items to a different memory position, it has lot less work to do if these are only small smart pointers. On top of that, there are some classes, which have expensive copy constructors (e.g. locking!). All of that can be avoided if the class instance is not copied at all.

这篇关于使用Manager和引导程序时出现内存错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆