【问题标题】:C++ memory - Do primitive-typed variables created with 'new' need to be deleted?C++ 内存 - 是否需要删除使用“新”创建的原始类型变量?
【发布时间】:2012-10-26 10:26:25
【问题描述】:

我对 C++ 的内存管理有点陌生。我读到如果你用new 关键字创建一个类,你必须delete 对象来释放它的内存。我还读到intcharbool 等原始类型是在堆栈上创建的,这意味着它们在超出范围时会被删除。

但是使用new 关键字创建的原始类型呢?我需要显式调用delete 吗?这些是像类一样在堆上创建的吗?或者由于它们是原始的,它们仍然是在堆栈上创建的吗?

我之所以问,是因为我正在使用 new 关键字分配 LPTSTR,但我担心如果我不调用 delete,内存将永远不会被释放。这是我在 cmets 中的简单问题的代码:

#include <Windows.h>
#include <tchar.h>
#include <string>

#ifdef _UNICODE
    typedef std::wstring Str;
#else // ANSI
    typedef std::string Str;
#endif

Str GetWndStr(HWND hwnd) {
    const int length = GetWindowTextLength(hwnd);

    if (length != 0) {
        LPTSTR buffer = new TCHAR[length + 1]; // Allocation of string
        GetWindowText(hwnd, buffer, length + 1);
        Str text(buffer);

        delete buffer; // <--- Is this line necessary?
        return text;
    } else {
        return _T("");
    }
}

我需要打电话给delete吗?不久前,我尝试使用GlobalAlloc()GlobalFree(),但是在运行时我收到一条错误消息,说明有关非法修改堆栈的信息,我没有确切的错误消息,因为这是不久前的。此外,除了您的回答之外,如果您想给我一些您认为有助于了解更多有关 C++ 内存管理的资源,那就太好了。

【问题讨论】:

  • 如果您使用的是 Java/C#,那么对于任何类型都不需要 delete。 :)
  • 类也在堆栈上创建。在这方面,原始类型和类没有什么不同。如果你新它,它在堆上,如果你不它,它在堆栈上(通常)。但是类可能有内部,所以类本身可能在堆栈上,但它使用的一些内部内存可能在堆上。
  • " 还读到原始类型,例如 int、char 和 bool,是在堆栈上创建的" - 要么你被骗了,要么你误解了你所读的内容。自动变量在堆栈上创建(这不能保证,它是一个实现细节),并在超出范围时销毁(这是有保证的)。 通常,不需要动态分配的原始类型对象,您可以为这些类型使用自动变量。但无论如何,你分配的东西没有原始类型,它是一个数组。属于TCHAR,属于原始类型。
  • 另外,std::string 不是原始类型
  • @Evgeni 是的,但 LPTSTRTCHAR 是。分配的是什么。

标签: c++ winapi memory memory-management allocation


【解决方案1】:

对于每个new,必须有一个delete,对于每个new[],必须有一个delete[]。注意new[]分配的内存必须用delete[]删除,贴出的代码不是这样。

可以使用智能指针,例如boost::scoped_array,它将在其析构函数(或reset() 函数)中执行delete[]。如果在调用 new[] 后可以抛出异常,这将特别有用:

boost::scoped_array<TCHAR> buffer(new TCHAR[length + 1]);
GetWindowText(hwnd, buffer.get(), length + 1);
Str text(buffer.get()); // buffer will be deleted at end of scope.

【讨论】:

  • +1 用于深入解释和智能指针使用示例。所以你是说既然我使用new TCHAR[length + 1],我需要使用delete[]
  • @BrandonMiller,是的。如果new[]delete[]
【解决方案2】:

您的数组是用new[] 分配的,因此必须用delete[] 删除(不是 delete)。

您也不需要显式动态分配:

Str text(length+1, 0);
GetWindowText(hwnd, &text[0], length + 1);
text.resize(length); // remove NUL terminator
return text;

在 C++03 中需要一些理由,stringwstring 是否实际上分配了连续的内存,适合作为缓冲区传递。 C++03 标准不保证这一点,但在 MSVC++ 中确实如此。如果你不想依赖这个事实,那么它保证矢量,所以你可以将它用于缓冲区:

std::vector<TCHAR> buffer(length+1);
GetWindowText(hwnd, &buffer[0], length + 1);
return Str(buffer.begin(), buffer.end() - 1);

在 C++ 中直接使用new[] 是非常罕见的。在这两种情况下,我的vectorstring 缓冲区都是一个自动变量,所以我不需要做任何特别的事情来确保它在其作用域结束时被正确销毁。

【讨论】:

  • +1 用于 std::vector 使用。提供与scoped_array 相同的功能,无需额外依赖。
  • +1 用于向量和字符串的使用示例。以及向量连续记忆的解释。
【解决方案3】:

是(除非您使用智能指针或类似的工具为您删除它)

【讨论】:

    【解决方案4】:

    是的,规则很简单。你用new分配的所有东西都需要用delete释放;并且使用new[] 分配的所有内容都需要使用delete[] 释放。

    为了减少出错的机会,最好使用容器、智能指针或其他RAII风格的对象来管理动态资源,而不是自己记住使用delete

    【讨论】:

    • 要添加到规则中,使用placement-new 构造的任何内容都必须在代码中显式调用其析构函数(如果它有析构函数)。 Placement-new 允许您在现有内存中构造一些东西,而不是分配新的内存块,例如 newnew[] 做。
    【解决方案5】:

    当然,无论分配什么类型。它还有内存空间。

    【讨论】:

      猜你喜欢
      • 2018-06-23
      • 2020-11-30
      • 2018-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      相关资源
      最近更新 更多