3

我知道这个问题的标题看起来有点伤脑筋,但我真的不知道如何用一句话来问这个问题。我只会告诉你我的意思:

void f(T *obj)
{
    // bla bla
}
void main()
{
    f(new T());
}

据我所知,(几乎)每个新的都需要一个删除,这需要一个指针(由新返回)。在这种情况下,new 返回的指针不会存储在任何地方。那么这会是内存泄漏吗?

C++ 是否有某种魔法(程序员不可见)在函数结束后删除对象,或者这种做法总是一个坏主意?

4

7 回答 7

7

没有特别的魔法,删除不会被自动调用。

这绝对不是“总是一个坏主意” - 如果函数以某种形式获得对象的所有权,那么它是调用此类函数的完全有效方式:

container.AddAndTakeOwnership(new MyItem(42));
于 2012-10-25T21:21:24.347 回答
5

是的,这总是一个坏主意。函数和构造函数应该明确它们的所有权语义,并且如果它们期望获取或共享传递的指针的所有权,它们应该分别接收std::unique_ptr或共享std::shared_ptr

有许多遗留 API 采用具有所有权语义的原始指针,即使在标准库中也是如此(例如,locale构造函数采用Facet *所有权),但任何新代码都应该避免这种情况。

即使在构造 a unique_ptror时,shared_ptr您也可以避免使用new关键字,在后一种情况下使用make_shared,在前一种情况下编写应该很快添加到语言中的make_unique函数模板。

于 2012-10-25T21:27:06.580 回答
5

在这种情况下,new 返回的指针不会存储在任何地方。那么这会是内存泄漏吗?

不,它不一定是内存泄漏。指针作为参数存储为f

void f(T *obj)
//        ^^^  here pointer is "stored"
{
    // bla bla
    delete obj; // no memory leak if delete will be called on obj
}
void main()
{
    f(new T());
  //  ^^^^^^^  this "value" will be stored as an argument to f
}

C++ 是否有某种魔法(程序员不可见)在函数结束后删除对象,或者这种做法总是一个坏主意?

在你的例子中没有魔法。正如我所展示的 - 必须明确调用删除。

更好的是使用智能指针,然后 C++“魔术”工作,并且不需要删除。

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
void main()
{
    f(new T());
}
于 2012-10-25T21:33:15.597 回答
4

显示的代码将导致内存泄漏。C++ 没有垃圾收集,除非您明确使用专门的框架来提供它。

其原因与 C/C++ 中管理内存的方式有关。对于局部变量,例如您的示例,直接从操作系统(malloc)请求对象的内存,然后指向对象的指针存在于堆栈中。由于 C/C++ 可以进行任意复杂的指针运算,编译器无法知道是否存在指向该对象的其他指针,因此在函数 f() 结束时无法回收内存。

为了自动防止泄漏,必须从托管堆中分配内存,并且必须仔细跟踪对该堆的每个引用以确定何时不再使用给定对象。为了获得这种能力,你必须放弃 C 的指针算术能力。

例如,假设编译器可以神奇地发现对 obj 的所有正常引用都已失效并删除了该对象(释放了内存)。如果你有一些非常复杂的 RUNTIME DEPENDENT 表达式,比如 void* ptr = (&& &&(&&& *obj)/2++ - currenttime() - 567 + 3^2 % 52) 等;编译器如何知道这个 ptr 是否指向 obj?没有办法知道。这就是没有垃圾收集的原因。您可以进行垃圾收集或复杂的运行时指针运算,而不是两者兼而有之。

于 2012-10-25T21:29:50.760 回答
2

这通常在您的函数 f() 将对象存储在某处时使用,例如数组(或任何其他数据容器)或简单的类成员;在这种情况下,删除将(并且必须)在其他地方进行。

否则不是一个好主意,因为无论如何您都必须在函数结束时手动删除它。在这种情况下,您只需声明一个自动(在堆栈上)对象并通过指针传递它。

于 2012-10-25T21:20:53.207 回答
1

没有魔法。在您的情况下, f 被称为 main 返回 CRT 的 main 后,操作系统最终将清理“泄漏”。这不一定是一个坏主意,它可能会赋予 f 所有权,并且由 f 来做事并最终删除。有些人称之为不好的做法,但它可能在野外。

编辑:虽然我认为该代码不比以下更危险:

void f(T *obj)
{
    // bla bla
}
void main()
{
    T* test = new T ();
    f(test);
}

主要是一样的。它对 f 说,这是一个指向某个内存的指针,它是你的,你现在照顾它。

于 2012-10-25T21:21:09.903 回答
1

在 C++ 中,不鼓励传递指针,因为没有相关的所有者语义(即,您无法知道谁拥有指针,因此谁负责删除指针)。因此,当您确实有一个函数(需要一个指针)时,您需要非常清楚地记录该函数是否负责清理指针。当然,像这样的文档很容易出错,因为用户必须阅读它。

在 C++ 程序中,传递描述事物所有权语义的对象更为正常。

  1. 通过引用传递
    函数没有取得对象的所有权。它只会使用该对象。
  2. 传递一个 std::auto_ptr (或 std::unique_ptr)
    函数正在传递指针和指针的所有权。
  3. 传递一个 std::shared_ptr
    函数正在传递指针的共享所有权。

通过使用这些技术,您不仅可以记录所有权语义,而且使用的对象还将自动控制对象的生命周期(从而使您的函数免于调用 delete)。

delete因此,在现代 C++ 代码中很少看到手动调用。

所以我会这样写:

void f(std::unique_ptr<T> obj)
{
    // bla bla
}
int main()
{
    f(std::unique_ptr<T>(new T()));
}
于 2012-10-25T21:49:47.120 回答