在C ++中使用指针向量动态分配的对象时,如何避免内存泄漏? [英] How to avoid memory leaks when using a vector of pointers to dynamically allocated objects in C++?

查看:87
本文介绍了在C ++中使用指针向量动态分配的对象时,如何避免内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用指向对象的指针向量.这些对象是从基类派生的,并且正在动态分配和存储.

I'm using a vector of pointers to objects. These objects are derived from a base class, and are being dynamically allocated and stored.

例如,我有类似的东西:

For example, I have something like:

vector<Enemy*> Enemies;

我将从Enemy类派生,然后为派生类动态分配内存,如下所示:

and I'll be deriving from the Enemy class and then dynamically allocating memory for the derived class, like this:

enemies.push_back(new Monster());

为了避免内存泄漏和其他问题,我需要注意些什么?

What are things I need to be aware of to avoid memory leaks and other problems?

推荐答案

std::vector 会像往常一样为您管理内存,但是该内存将是指针的,而不是对象的.

std::vector will manage the memory for you, like always, but this memory will be of pointers, not objects.

这意味着一旦向量超出范围,您的类将在内存中丢失.例如:

What this means is that your classes will be lost in memory once your vector goes out of scope. For example:

#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

} // leaks here! frees the pointers, doesn't delete them (nor should it)

int main()
{
    foo();
}

您需要做的是确保在向量超出范围之前删除所有对象:

What you'd need to do is make sure you delete all the objects before the vector goes out of scope:

#include <algorithm>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<base*> container;

template <typename T>
void delete_pointed_to(T* const ptr)
{
    delete ptr;
}

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(new derived());

    // free memory
    std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}

int main()
{
    foo();
}

但是,这很难维护,因为我们必须记住要执行一些操作.更重要的是,如果在元素分配和释放循环之间发生异常,则释放循环将永远不会运行,并且您仍然会陷入内存泄漏!这就是所谓的异常安全性,这是需要自动进行重新分配的关键原因.

This is difficult to maintain, though, because we have to remember to perform some action. More importantly, if an exception were to occur in-between the allocation of elements and the deallocation loop, the deallocation loop would never run and you're stuck with the memory leak anyway! This is called exception safety and it's a critical reason why deallocation needs to be done automatically.

更好的是,如果指针自身删除了.这些称为智能指针,标准库提供了 std::unique_ptr std::shared_ptr .

Better would be if the pointers deleted themselves. Theses are called smart pointers, and the standard library provides std::unique_ptr and std::shared_ptr.

std::unique_ptr表示指向某些资源的唯一(非共享,单所有者)指针.这应该是您的默认智能指针,并且可以完全替代任何原始指针的使用.

std::unique_ptr represents a unique (unshared, single-owner) pointer to some resource. This should be your default smart pointer, and overall complete replacement of any raw pointer use.

auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself

由于疏忽,C ++ 11标准中缺少

std::make_unique,但是您可以自己制作.要直接创建unique_ptr(如果可能,不建议在make_unique上不推荐使用),请执行以下操作:

std::make_unique is missing from the C++11 standard by oversight, but you can make one yourself. To directly create a unique_ptr (not recommended over make_unique if you can), do this:

std::unique_ptr<derived> myresource(new derived());

唯一指针仅具有移动语义;它们不能被复制:

Unique pointers have move semantics only; they cannot be copied:

auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty

这就是我们需要在容器中使用的所有内容:

And this is all we need to use it in a container:

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::unique_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(make_unique<derived>());

} // all automatically freed here

int main()
{
    foo();
}

shared_ptr具有引用计数的副本语义;它允许多个所有者共享对象.它跟踪一个对象存在多少个shared_ptr,并且当最后一个不存在时(该计数变为零),它释放指针.复制只会增加参考计数(并且以较低的几乎免费的成本转移所有权).您可以使用std::make_shared进行创建(或直接如上所示,但是由于shared_ptr必须在内部进行分配,因此使用make_shared通常更有效,并且从技术上讲,异常安全性更高).

shared_ptr has reference-counting copy semantics; it allows multiple owners sharing the object. It tracks how many shared_ptrs exist for an object, and when the last one ceases to exist (that count goes to zero), it frees the pointer. Copying simply increases the reference count (and moving transfers ownership at a lower, almost free cost). You make them with std::make_shared (or directly as shown above, but because shared_ptr has to internally make allocations, it's generally more efficient and technically more exception-safe to use make_shared).

#include <memory>
#include <vector>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

typedef std::vector<std::shared_ptr<base>> container;

void foo()
{
    container c;

    for (unsigned i = 0; i < 100; ++i)
        c.push_back(std::make_shared<derived>());

} // all automatically freed here

int main()
{
    foo();
}

请记住,您通常希望使用std::unique_ptr作为默认值,因为它更轻巧.另外,std::shared_ptr可以用std::unique_ptr构造(反之亦然),所以从小处开始是可以的.

Remember, you generally want to use std::unique_ptr as a default because it's more lightweight. Additionally, std::shared_ptr can be constructed out of a std::unique_ptr (but not vice versa), so it's okay to start small.

或者,您可以使用创建的容器来存储指向对象的指针,例如 boost::ptr_container :

Alternatively, you could use a container created to store pointers to objects, such as a boost::ptr_container:

#include <boost/ptr_container/ptr_vector.hpp>

struct base
{
    virtual ~base() {}
};

struct derived : base {};

// hold pointers, specially
typedef boost::ptr_vector<base> container;

void foo()
{
    container c;

    for (int i = 0; i < 100; ++i)
        c.push_back(new Derived());

} // all automatically freed here

int main()
{
    foo();
}

虽然boost::ptr_vector<T>在C ++ 03中有明显的用途,但现在我不能说出相关性,因为我们可以使用std::vector<std::unique_ptr<T>>,而几乎没有或没有可比的开销,但是应该对此主张进行检验.

While boost::ptr_vector<T> had obvious use in C++03, I can't speak of the relevance now because we can use std::vector<std::unique_ptr<T>> with probably little to no comparable overhead, but this claim should be tested.

无论如何,永远不要显式地释放代码中的内容.整理一下内容以确保自动处理资源管理.您的代码中不应包含原始拥有指针.

Regardless, never explicitly free things in your code. Wrap things up to make sure resource management is dealt with automatically. You should have no raw owning pointers in your code.

作为游戏的默认设置,我可能会选择std::vector<std::shared_ptr<T>>.无论如何,我们都希望共享,它的速度足够快,直到性能分析另有说明为止,它是安全的,并且易于使用.

As a default in a game, I would probably go with std::vector<std::shared_ptr<T>>. We expect sharing anyway, it's fast enough until profiling says otherwise, it's safe, and it's easy to use.

这篇关于在C ++中使用指针向量动态分配的对象时,如何避免内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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