如何正确处理垃圾收集并避免使用自定义对象指针集合的内存泄漏...?

How do I properly handle garbage collection and avoid memory leaks with a collection of custom object pointers...?

所以我试图牢牢掌握我应该如何编写对象和指针集合的代码,而不会导致内存泄漏...我想做这样的事情...

class StackTest
{
    public:
        StackTest() : m_id(-1), 
           m_name("")
        { };
        StackTest(const int id, 
           const std::string name) : m_id(id), 
           m_name(name)
        { };

        void SomeFunctionThatCanPreformTasksOnTHISStackTest();
    private:
        const int m_id;
        const std::string m_name;
}

class StackTestCollection
{
    public:
        void AddStackTest(const StackTest* test);

        void SomeFunctionThatCanPreformTasksOnAllStackTests();
    private:
        std::vector<StackTest*> m_stack_tests;
}

然后我为集合提供函数 AddStackTest(并创建对集合和单个 StackTest 执行操作的函数)...

void StackTestCollection::AddStackTest(const StackTest* test)
{
    m_stack_tests.push_back(test);
}

最后是我的主要代码库...

// This scopes my_collection so that after the '}' my_collection should get deleted...
{
    StackTestCollection my_collection;
    my_collection.AddStackTest(new StackTest(0, "First Stack Test"));
    my_collection.AddStackTest(new StackTest(0, "Second Stack Test"));
    my_collection.AddStackTest(new StackTest(0, "Third Stack Test"));
}

现在我意识到我需要对存储在 StackTestCollection 中的 StackTest 指针调用 delete。我想我可以将它添加到 StackTestCollection....

~StackTestCollection()
{
    for(auto &test : m_stack_tests)
    {
        delete test;
    }
}

现在这让我问了几个问题...

1) 当我们删除堆中的数据时,这是否会导致程序在循环期间崩溃,或者向量是否仍然稳定,因为堆栈中的指针仍然存在,但是是一个悬空指针.. .

2) 我是否还需要 运行 删除集合 m_stack_tests 向量?像这样...

delete m_stack_tests;

3) 在我的主要代码中,我创建了集合 (StackTestCollection my_collection;),据我了解,我只需要用指针控制垃圾....所以因为 my_collection 不是一个指针,我不需要做类似这样的事情....对吗?

delete my_collection;

我想指出,此示例程序将 运行 运行在一个大型多线程服务器中,用户在该服务器上登录并分配他们自己的集合。 (所以每个用户在登录时都有自己的集合,在注销时与用户相关的数据也应该被删除)。

此外,我没有使用 C++11,所以没有 unique_ptr 的 :P.

将您的向量定义为指向您的类型的共享指针的向量。这将允许共享指针的析构函数在从向量中删除值时销毁对象。

您可以做两件事:

  1. 制作容器std::vector<StackTest> m_stack_tests;。无需内存处理。只需将您的函数更改为采用 const StackTest&StackTest&&
  2. 制作容器std::vector<std::unique_ptr<StackTest>> m_stack_tests;。现在,有内存处理,但 unique_ptr 会为您处理一切,因此您不必担心。

在这两种情况下,您不必在任何地方写 delete,这很棒。按照你写的方式,你 必须 delete 向量中的每个元素一个一个地...但是你 不会 必须 delete 向量本身。它不是 newed。

1:只要没有其他东西(例如另一个线程)在删除后尝试访问堆资源,这就不会导致问题。

2:用new创建的东西只需要delete即可

3:你说不需要删除 my_collection 是对的,但假设必须删除每个指针是错误的,请考虑以下代码:

void Foo(int* ptr)
{
    // do something
}

int i = 1234;
Foo(&i); // passing a pointer to i which is a stack based object

我建议使用智能指针来管理堆 allocations/deallocations 并避免完全使用 new 和 delete。在您的情况下,您可以使用 unique_ptr

的向量

一般来说,删除那段记忆安全。

原因是指针是从外部源传入的,你不知道它们来自哪里(好吧,你,但是当你写接口时你应该假装你不知道)。管理该内存是调用者的责任。这就是为什么 std::vector 在它被破坏时不为你释放内存的原因。

另一方面,您的 class 不安全,因为该指针的其他持有者在您背后删除它。这两个问题的解决方案是在 AddStackTest 函数中复制数据,但这可能效率低下。

当然,如果你说,在你上面的评论里class,

/* This class takes ownership of the objects passed in, and will delete
   them upon destruction.  */

...现在您已经更改了规则,调用者必须采取措施复制对象,如果这不是他们想要的。

有多种方法可以实现这种破坏。像您这样的显式析构函数将起作用,因此将使您的向量使用 std::unique_ptr.

顺便说一句,你的 AddStackTest 需要 const StackTest*,但你存储 StackTest *,这似乎是错误的(编译器应该警告你),除非你真的在那里制作副本.