如何正确处理垃圾收集并避免使用自定义对象指针集合的内存泄漏...?
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.
将您的向量定义为指向您的类型的共享指针的向量。这将允许共享指针的析构函数在从向量中删除值时销毁对象。
您可以做两件事:
- 制作容器
std::vector<StackTest> m_stack_tests;
。无需内存处理。只需将您的函数更改为采用 const StackTest&
或 StackTest&&
。
- 制作容器
std::vector<std::unique_ptr<StackTest>> m_stack_tests;
。现在,有内存处理,但 unique_ptr
会为您处理一切,因此您不必担心。
在这两种情况下,您不必在任何地方写 delete
,这很棒。按照你写的方式,你 会 必须 delete
向量中的每个元素一个一个地...但是你 不会 必须 delete
向量本身。它不是 new
ed。
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 *
,这似乎是错误的(编译器应该警告你),除非你真的在那里制作副本.
所以我试图牢牢掌握我应该如何编写对象和指针集合的代码,而不会导致内存泄漏...我想做这样的事情...
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.
将您的向量定义为指向您的类型的共享指针的向量。这将允许共享指针的析构函数在从向量中删除值时销毁对象。
您可以做两件事:
- 制作容器
std::vector<StackTest> m_stack_tests;
。无需内存处理。只需将您的函数更改为采用const StackTest&
或StackTest&&
。 - 制作容器
std::vector<std::unique_ptr<StackTest>> m_stack_tests;
。现在,有内存处理,但unique_ptr
会为您处理一切,因此您不必担心。
在这两种情况下,您不必在任何地方写 delete
,这很棒。按照你写的方式,你 会 必须 delete
向量中的每个元素一个一个地...但是你 不会 必须 delete
向量本身。它不是 new
ed。
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 *
,这似乎是错误的(编译器应该警告你),除非你真的在那里制作副本.