C ++指针和内存泄漏

时间:2015-09-18 13:20:33

标签: c++ pointers memory-leaks

我想说明一件我曾见过的事情:

class Something {};

void do_something(const Something *p_sth) {}

然后:

do_something(new Something());

应该导致内存泄漏,因为当您致电时,您还应该始终致电删除,对吗?这会是一个很好的解决方案吗?

void do_something(const Something *p_sth)
{
    delete p_sth;
}

或者更好地使用参考文献&?我也发现智能指针可以解决这个问题,所以删除并不是必需的(这似乎是一件好事,但我之前从未使用过它)。我只是想知道最好的解决方案,以避免内存泄漏。感谢

*谢谢大家的回答。它帮助我清理了一些东西。我也很抱歉我发布的代码可能过于笼统。

5 个答案:

答案 0 :(得分:1)

您建议承担指针的所有权并删除对象存在问题。

这种行为在c ++中不是惯用的。程序员希望他们必须删除他们使用new分配的每个对象。如果您的函数的用户期望他们负责删除他们传递给函数的地址的对象,那么您的解决方案就会分崩离析。此外,它会阻止在调用结束后必须保持存在的对象中使用您的函数:

Something* s = new s();
do_something(s);
s.foo() // oops, object was deleted, I can't use it anymore
delete s; // oops, double delete because I'm for some reason not responsible for deleting the object that I allocated

您的解决方案还会阻止使用具有该功能的自动和静态分配的对象。

Something s;
do_something(&s); //oops, do_something assumes that object is dynamically allocated

所有这些警告都必须记录在函数的用户身上。

没有删除函数内部的原始指针没有这些问题。管理呼叫者内存应该不是您的功能的责任。这样做会破坏单一责任原则。当想要传输或共享所有权时,原始指针参数没有任何问题。如果您确实希望改变所有权,那么请使用专为此设计的智能指针。

智能指针没有上述某些问题,但它们仍然阻止使用带有自动和静态对象的函数。

在许多情况下,参考参数是理想的。它告诉调用者他们仍然负责该对象。作为奖励,不需要addressof运算符允许稍微更好的语法。当然,来电者可能仍然忘记管理他们的记忆,但正如我所指出的,这不应该是你的责任。

参考文献有一个潜在的缺点。它们不能为空。如果你不需要null,那么它实际上是一个优势。

哪种解决方案是理想的,取决于您的需求。以下情况并非适用于所有极端情况,但应适用于大多数常见情况:

  • 如果要修改对象,则传递引用。
    • 除非你需要null,否则使用指针
  • 如果您只是想阅读该对象,那么
    • 如果对象是可复制的,小的(小于或等于单词),不会指向动态对象而不是多态,那么按值传递
    • 否则或如果您因为编写模板而不知道这些内容,请传递const引用
    • 除非你需要null,否则使用指针
  • 如果您想要转移所有权,请使用unique_ptr
  • 如果您希望共享所有权,请使用shared_ptr

答案 1 :(得分:1)

最好是使用智能指针

class Something {};

void do_something(std::shared_ptr<Something> p_sth) 
{
...
}

这样一来,当您查看原型时,所有权是明确的,并且当您离开范围时,您将获得自动删除。

答案 2 :(得分:0)

  

我只是想知道什么是最好的解决方案,以避免内存泄漏。感谢

确保没有内存泄漏的最佳解决方案是使用std::shared_ptr这是一个智能指针,只要它没有任何引用,就会删除它自己。如果您具有多个对同一指针的引用,则此指针最佳。如果在给定时间只有一个引用,请使用std::unique_ptr

这可以防止内存泄漏,我也更喜欢使用智能指针而不是标准指针,因为它们非常强大。另外,保留问题:

  

或者使用引用&amp;?

更好

在任何需要引用对象的地方使用引用,如果删除引用,作为智能指针,它也会删除指针(因为没有其他对该指针的引用) < / p>

我希望这能回答你的问题:)

答案 3 :(得分:0)

希望避免使用原始指针。

如果您不需要堆分配并且可以使用堆栈分配变量,那么更喜欢通过引用传递(或者如果适当的移动构造函数到位,则甚至按值传递,但这是另一天的主题)。 / p>

如果您需要堆分配(用于多态,依赖注入,信息隐藏,所有权转移等),那么确定该对象的所有权语义是什么,并使用适当的智能指针来管理这些语义。

如果所有其他方法都失败了并且您必须使用原始指针(可能是您正在处理遗留接口或C接口,或类似的东西),那么再次明确定义您的所有权语义并记录它们以明确谁负责用于删除堆分配的对象等

如果必须有一个指向堆栈分配对象的原始指针,那么请记录您不会删除指针,并注意记录指针的生命周期,以避免在超出范围后访问该对象。

答案 4 :(得分:0)

最干净的解决方案是完全避免指针。 是的,参考是一个好主意。

void do_something(const Something &p_sth) {}

然后你可以在堆栈上声明你的'某事'。

void function_that_does_something()
{
    Something mySomething;

    do_something( mySomething );
}

最好让编译器为你做清理工作。