如何安全地从内存中清除使用 new 关键字创建的对象(带有属性)?

Posted

技术标签:

【中文标题】如何安全地从内存中清除使用 new 关键字创建的对象(带有属性)?【英文标题】:How do you safely clear an object from memory (with attributes) which was created using the new keyword? 【发布时间】:2021-09-04 13:00:18 【问题描述】:

据我所知,每个“新”调用都需要对该对象进行相应的“删除”调用。那么这真的正确吗?:

using namespace std;

class Box 
   public:
      double length;
      char letters_in_box[80];
;

int main() 
   Box *b = new Box;
   b->length = 2.0;
   b->letters_in_box = "Hello world";

   //Some code with b

   delete b;

   return 0;

与“length” double 和“letters_in_box”数组关联的内存是否被此清除?

【问题讨论】:

我猜你的意思是“来自另一个进程”,然后答案是操作系统特定的。请在您的问题中提供一些minimal reproducible example,并注意C++ rule of five。你可能想定义一个 C++ 析构函数 无法保证,我认为你不应该问自己这个问题。 我认为b->letters_in_box = "Hello world"; 不会编译,因为letters_in_box 是一个数组。 假设所有这些都发生在同一个函数或块中,那么您可能根本不想使用动态存储持续时间。而是使用auto b = Box(); 你也应该使用std::string。鉴于您的简单代码存在很多问题,我强烈建议您阅读一些有关 C++ 的书籍。 C++ 不是即兴创作的好语言,因为有很多可能的陷阱。 【参考方案1】:

是的。当您删除 b 时,它也会删除 letters_in_box 数组。

但是,对于你的b->letters_in_box = "Hello world";,你会得到一个编译错误:“error C3863: array type 'char [80]' is not assignable”

#include <memory> // For 'memcpy_s' (since C11)

class Box

public:
    double length;
    char letters_in_box[80];
;

int main()

    Box* b = new Box;

    b->length = 2.0;
    // b->letters_in_box = "Hello world"; ** Compile Error C3863: array type 'char [80]' is not assignable **
    memcpy_s(b->letters_in_box, sizeof(b->letters_in_box), "Hello world", sizeof("Hello world"));

    // Some code with b

    delete b;

现代 C++

new 更好的做法是智能指针,例如,在出现异常的情况下您根本不必担心删除:

#include <memory> // For 'std::unique_ptr' and for 'memcpy_s'

class Box

public:
    double length;
    char letters_in_box[80];
;

constexpr char my_text[] = "Hello world"; 

int main()

    auto b = std::make_unique<Box>(); // No need to delete

    b->length = 2.0;
    memcpy_s(b->letters_in_box, sizeof(b->letters_in_box), my_text, sizeof(my_text));

    // Some code with b

另外,(而不是 C 数组)我更喜欢使用 C++ 数组

#include <array>  // For 'std::array'
#include <memory> // For 'std::unique_ptr' and for 'memcpy_s' 

class Box

public:
    double length;
    std::array<char, 80> letters_in_box;
;
   
constexpr char my_text[] = "Hello world";

int main()

    auto b = std::make_unique<Box>(); // No need to delete

    b->length = 2.0;
    memcpy_s(&b->letters_in_box, b->letters_in_box.size(), my_text, sizeof(my_text));

    //Some code with b

--

最后一条评论:如果不限制使用char[],我会改用std::string

#include <string> // For 'std::string'
#include <memory> // For 'std::unique_ptr' 

class Box

public:
    double length;
    std::string letters_in_box;
;

int main()

    auto b = std::make_unique<Box>(); // No need to delete

    b->length = 2.0;
    b->letters_in_box = "Hello world";

    //Some code with b

【讨论】:

memcpy 具有未定义的行为,因为它访问的 char 远远超过了 C 字符串文字的结尾。 @Eljay 感谢您的评论。我切换到 memcpy_s。【参考方案2】:

在现代 C++ 中,强烈建议不要使用 new 和 delete,因为 Karen 在上面提到过,如果在程序期间由于任何原因发生异常,并且您的异常处理会导致不返回运行删除的分支,则会导致内存泄漏。

相反,我会考虑使用智能指针 (https://en.cppreference.com/book/intro/smart_pointers) 来创建指向所用对象的新指针,因为一旦它们超出范围(唯一指针)(即异常分支),它们将允许释放内存然后不返回相同的范围),或者一旦它们的“引用”数量到期(共享指针)。

关于您编写​​的显式代码,是的,一旦该类被删除,“length”双精度和“letters_in_box”数组将被删除,但重要的是要注意,如果您显式使用了 malloc/calloc/etc为了分配内存,则应使用类析构函数来释放此内存。

编辑:也强烈建议阅读 RAII (https://en.cppreference.com/w/cpp/language/raii),因为它确实是现代、安全 C++ 的基础。

【讨论】:

【参考方案3】:

那么这真的正确吗?

是的,没错。但是,在删除b之前,你必须确保它没有抛出异常。

例如:

Box *b = new Box;
b->length = 2.0;
b->letters_in_box = "Hello world";

throw 5; //this is not usually done.

delete b;

在此示例中,如果在调用delete 之前未处理异常,则会出现memory leak。

【讨论】:

那么如何保证即使出现异常也被删除呢? @CoderAlpha,你必须在调用delete之前处理异常。 @CoderAlpha 最好的办法是完全避免使用newdelete。在现代 c++ 中,您很少需要 newdelete。如果您创建的对象应该只存在于函数或块中,只需编写Box b = Box();。如果您需要转让所有权,您可以使用unique_ptr,如果unique_ptr 不符合您的需求,您可以使用shared_ptr 所以Box b = Box(); 不需要任何删除,因为它在堆栈上,对吧?但是如果我们需要比堆栈大小更多的内存怎么办? @CoderAlpha 是的。如果Box 包含的数据量可能超过堆栈,您可以使用unique_ptr (std::unique_ptr&lt;Box&gt; b = std::make_unique&lt;Box&gt;();)。或者将Box 的一个(或多个)成员更改为不同的名称,例如std::vector。但是选择什么解决方案取决于实际用例。

以上是关于如何安全地从内存中清除使用 new 关键字创建的对象(带有属性)?的主要内容,如果未能解决你的问题,请参考以下文章

javabean 如何清除已经填充了的属性

安全清除内存并重新分配

Java关键字new-----对象的内存分配原理

如何在 VB.Net 中正确地从内存中删除控件? [复制]

基础回顾——Java对象创建内存分配访问定位概述

怎么使用new操作符创建动态数组?