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

例如,我有类似的东西:

vector<Enemy*> Enemies;

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

enemies.push_back(new Monster());

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

有帮助吗?

解决方案

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>>. 。无论如何,我们期望共享,它足够快,直到分析表明不然,它是安全的,并且易于使用。

其他提示

我假设如下:

  1. 你有一个像向量<base*>这样的向量
  2. 在堆上分配对象后,您将指针推送到该向量
  3. 您想要将衍生*指针推回到该向量中。

我想到以下几点:

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

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

您需要做的是使用智能指针:

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

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

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

有一点要非常小心是如果有两个怪兽()派生的对象,其内容是在值相同。假设你想从你的载体(基类指针到派生妖怪对象)删除重复的怪物对象。如果您使用的是标准的方言来删除重复(排序的,独特的,抹去:见链接#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 /独特/

2)@SEE_ALSO:什么是最有效的方式来擦除重复和排序的矢量?

第二链路被良好地写入,并且将用于一个std ::矢量工作,但具有内存泄漏,重复的FreeS(有时导致违反分割)为一个std ::矢量

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top