如何使用指针的载体能够在C ++动态分配对象时以避免内存泄漏?(How to avoid memor

2019-06-21 08:12发布

我使用对象指针的载体。 这些目的是从一个基类派生,并正在动态分配和储存。

例如,我有这样的:

vector<Enemy*> Enemies;

我会从敌人类中派生,然后派生类的动态分配内存,如下所示:

enemies.push_back(new Monster());

有什么事情,我需要知道,以避免内存泄漏和其他问题?

Answer 1:

std::vector将负责管理该内存为你,像往常一样,但这种记忆将是指针,不是对象。

这意味着你的类将在内存中迷失了你的矢量超出范围。 例如:

#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();
}

什么你需要做的就是确保你删除所有对象的矢量超出范围之前:

#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();
}

这是难以维持,不过,因为我们必须记住执行一些动作。 更重要的是,如果一个例外是发生在中间单元的分配和释放循环中,释放循环永远不会运行,你坚持的内存泄漏呢! 这就是所谓的异常安全,这也是为什么释放需要自动完成的关键原因。

更好的是,如果指针删除自己。 论文被称为智能指针,标准库提供std::unique_ptrstd::shared_ptr

std::unique_ptr代表一个独特的(非共享,单老板)指向一些资源。 这应该是默认的智能指针,和整体完全更换任何原始指针使用。

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

std::make_unique是由监督C ++ 11标准缺失,但你可以自己做一个。 要建立一个直接unique_ptr (不建议超过make_unique如果可以的话),这样做:

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

独特的指针有移动语义只; 他们不能被复制:

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

而这就是我们需要在容器中使用它:

#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 S表示物体存在,当最后一个不再存在(即计数为零),它释放的指针。 复制只是增加引用计数(和在一个较低的,几乎没有成本移动所有权转移)。 你让他们std::make_shared (或直接如上图所示,但由于shared_ptr有使内部分配,这是通常更高效,技术上更是异常安全使用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 (而不是相反),所以它没关系从小事做起。

另外,您也可以使用创建存储对象的指针,如容器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>>有可能很少或几乎没有可比性开销,但这种说法应该被测试。

无论如何, 在代码中从未明确免费的东西 。 包东西,以确保资源管理处理自动。 你应该在你的代码中没有原所属指针。

比如在游戏中默认的,我可能会一起去std::vector<std::shared_ptr<T>> 。 我们预计,反正共享,它的速度不够快,直到分析说,否则,它是安全的,而且很容易使用。



Answer 2:

我假设如下:

  1. 您有像矢量的矢量<基地*>
  2. 您分配上堆上的对象后推指向这个矢量
  3. 你想干什么衍生*指针的的push_back到该载体。

下面的事情来我的脑海:

  1. 矢量不会释放对象的内存指针指向。 你必须删除它本身。
  2. 没有什么特定的载体,但基类的析构函数应该是虚拟的。
  3. 向量<基地*>和矢量<衍生*>是两个完全不同的类型。


Answer 3:

使用麻烦的vector<T*>是,每当矢量超出范围意外地(当一个异常被抛出等),所述载体后自己清理,但这只会释放它管理用于保持指针的存储器,不是你的什么指针指分配的内存。 所以GMAN的delete_pointed_to功能是有限的价值,因为它仅当一切正常工作。

什么,你需要做的是使用智能指针:

vector< std::tr1::shared_ptr<Enemy> > Enemies;

(如果你的标准库来没有TR1,使用boost::shared_ptr ,而不是。)除了非常罕见的情况(循环引用)这只是删除对象生存期的麻烦。

编辑 :请注意,GMAN,他详细的解答,提起这个。



Answer 4:

有一点要非常小心是如果有两个怪物()派生的对象,其内容是价值相同。 假设你想从你的载体(基类指针到派生妖怪对象)删除重复的怪物对象。 如果您使用的是标准的方言来删除重复(排序的,独特的,抹去:见链接#2],你会遇到内存泄漏问题,和/或重复删除的问题,可能导致分割VOIOLATIONS(我曾亲自看到这些问题LINUX机)。

与STD中的问题::独特()是在[duplicatePosition,端重复的)范围[包容,独占)在该载体的末端是不确定的为○。 什么可能发生的是,那些不确定的((?)的项目可能是多余的重复或缺失的重复。

问题是,性病::唯一的()不是用来妥善处理指针的载体。 其原因是,从矢量“向下”向矢量的开始结束的std ::唯一副本的唯一身份。 对于纯的对象的矢量这个调用COPY CTOR,如果COPY CTOR被正确地写入,有内存泄漏的没有问题。 但是,当它的一个指针的向量,还有比“按位拷贝”其他没有COPY CTOR,所以指针本身是简单地复制。

有办法解决比使用智能指针等,这些内存泄漏。 写的std ::唯一的()的自己稍微修改后的版本为“your_company ::唯一的()”的一种方式。 基本的诀窍是,而不是复制一个元素,你会交换两个元素。 而且你必须确保的,而不是比较两个三分球,你叫BinaryPredicate下面的两个指针对象本身,而比较这两个“怪物”派生的对象的内容。

1)@SEE_ALSO: http://www.cplusplus.com/reference/algorithm/unique/

2)@SEE_ALSO: 什么是消除重复和排序向量的最有效方法是什么?

第二个环节是很好写的,会为一个std :: vector的工作,但有内存泄漏,重复的FreeS(有时会导致分割冲突)为一个std ::矢量

3)@SEE_ALSO:的valgrind(1)。 在Linux中,这“内存泄漏”的工具是什么,它可以找到令人惊叹! 我强烈建议使用它!

我希望张贴“my_company ::()独特的”在未来的后一个很好的版本。 眼下,它不是完美的,因为我想有BinaryPredicate来为任何一个函数指针或仿函数无缝地工作,我在同时处理恰当一些问题3 ARG版本。 如果我解决不了这些问题,我会后我有什么,并让社会上有改进什么我迄今所做的一展身手。



文章来源: How to avoid memory leaks when using a vector of pointers to dynamically allocated objects in C++?