【问题标题】:Do I have to use delete to avoid memory leaks? I have used new to allocate the structure instances我必须使用删除来避免内存泄漏吗?我已经使用 new 来分配结构实例
【发布时间】:2014-09-14 20:51:29
【问题描述】:

以下代码实现了一个简单的哈希函数。我已经使用 new 运算符分配了结构实例。

程序结束时我是否使用了delete 运算符?

如果是这样,我该怎么做? [每个结构实例的delete 语句?或者有更简单的方法吗? ]

#include <iostream>
#define SIZE 10

using namespace std;

typedef struct myhashtag
{
   int data;
   struct myhashtag * next;
} myhash;
void printhash(myhash array[])
{
// print fn
}

void hash(int data, myhash array[])
{
   int h = data % SIZE;
   myhash * newhash = new myhash;
   newhash->data = data;
   newhash->next = NULL;

   if(array[h].next == NULL)  //first insert
   {
      array[h].next = newhash;
      return;
   }
   myhash * iter = array[h].next;
   while(iter->next != NULL)
   {
      iter = iter->next;
   }
   iter->next = newhash;
   return;
}


int main()
{
   myhash array[SIZE];int i;
   for(i=0; i<SIZE; i++)
   {
      array[i].data = 0;
      array[i].next = NULL;
   }

   while(1)
   {
      cout << "\nSo, what would you like to enter? : ";
      cin >> i;
      hash(i, array);
      printhash(array);
   }

// the deletes go here 

   return 0;
}

【问题讨论】:

  • 每个发出的new() 都应该有一个随附的delete 声明。 '对于结构的每个实例都有一个delete 语句?' 是的,除了使用myhash array[SIZE]; 自动创建的那些。
  • 在处理大项目时,每个new运算符都需要使用delete!否则你会耗尽内存!
  • 但实际上您应该使用像std::shared_ptr 这样的内存管理设备,这样就不需要newdelete
  • @Sathish 如果有人可以对 cme​​ts 投反对票,我只会对你的第一句话投反对票。这只是不好的做法,可能会让人们养成总是忘记它的习惯。此外,一些项目可能从小型应用程序开始,并意外地(重新)用于更大的应用程序。然后你就会面临追踪所有动态分配的维护噩梦......
  • @anderas 实际上,他的陈述是 100% 正确的。智能指针是这样做的一种方法,在某些情况下这是一个很好的解决方案(但可能不是在这里——他需要将他的哈希表包装在一个更大的函数中来管理它的内存)。

标签: c++ memory-management memory-leaks destructor


【解决方案1】:

一般来说,您确实需要为每个使用 new 的分配执行一次删除(或数组的 delete[])。

顺便说一句,如果您使用的是 linux,valgrind 可以帮助检测内存泄漏。

最近的 C++ 版本以及 Boost 库都提供了各种智能指针,当对象不再被引用时,它们基本上会自动释放内存。

在您选择的 C++ 书中查找 std::unique_ptr。

【讨论】:

  • 谢谢!但实际上我不想使用任何库。 Boost 肯定会“增加”我的可执行文件的大小! [题外话] 无论如何,我认为 deletenew 应该可以完成这项工作?
  • @sigmasquared '但是我不想使用任何库'你不需要,除非 c++ 标准库对你来说算数。
  • 是的,它会,但有时很难知道何时需要删除。您必须在代码中正确记录调用者或被调用者是否负责释放内存。一种约定是当内存仍然属于调用者时使用引用,而当被调用者取得所有权时使用指针。
  • 再次查看他的代码,对于这个简单的案例,std::unique_ptr 实际上可以解决问题。如果他稍后决定重新散列以更改大小,那将变得更加复杂;在全局范围内,在这种情况下,通常最好将表包装在一个类中,并让它管理内存。
  • 没错。标准智能指针使用引用计数,这实际上意味着一个循环意味着不会回收任何内存。其他语言具有可以处理此类循环的垃圾收集器,但 C++ 没有。通常,使用标准 STL 容器可以更轻松地处理内存管理。也可以在一个池中分配所有内存,一次释放所有池,而不是单独释放每个元素(代码类似于 a = new(pool) Data)
【解决方案2】:

是的,您应该始终删除您分配的所有动态内存。

您可以实现一个简单的 RAII 容器来处理您的内存清理。只需创建一个类作为句柄,在其构造函数中通知内存。在其析构函数中删除它并提供某种访问机制。这样,任何内存都会在声明句柄的范围结束时自动清理。

【讨论】:

  • 没有课程的任何其他方式?我想我必须死记硬背,遍历所有元素并从前端删除它们,这样才能更快。
  • 有什么理由不使用类吗? c++ 类和结构之间唯一真正的区别是默认访问级别。结构默认为公共,类默认为私有。所以你可以对结构做同样的事情。
【解决方案3】:

C++ 确实需要您管理对象的生命周期。它并不像许多 Java 或 C# 书籍可能让您相信的那么难。但是,是的,如果不使用 garbage collector,您需要确保正确调用 delete

对于您发布的代码,我可以想象有两种方法可以做到这一点。第一个是,正如其他人所建议的,使用std::unique_ptr

第二个是给你的struct一个析构函数。我不太清楚你为什么使用经典的 C typedef struct ... 成语(你在 C 中这样做是为了你不必经常使用 struct 关键字,但你不必使用 @987654327 @ 关键字在 C++ 中经常出现)。我会将它保留在下面的代码中,但我想将其标记为非惯用:

typedef struct myhashtag
{
   int data;
   struct myhashtag * next;

   ~myhashtag()
   {
      if (next) {
         delete next;
      }
   }
} myhash;

(您仍然需要确保delete 放置“删除发生在这里”的评论的头节点)。

但是,这假定next 在列表末尾正确分配给NULL。您发布的代码可以做到这一点,但它只是按惯例强制执行。一个稍微惯用的方法是:

struct hash_node {
   int data;
   hash_node* next;

   hash_node(int d, n = NULL) : data(d), next(n) { }

   ~hash_node()
   {
      if (next) {
         delete next;
      }
   }
};

void hash(int data, hash_node* array)
{
   int h = data % SIZE;
   hash_node* newhash = new hash_node(data);

   if(array[h].next == NULL)  //first insert
   {
      array[h].next = newhash;
      return;
   }
   hash_node* iter = array[h].next;
   while(iter->next != NULL)
   {
      iter = iter->next;
   }
   iter->next = newhash;
   return;
}

更惯用的方法是:

#include <list>
#include <iostream>
using namespace std'

int main()
{
    list<int> list_that_the_code_was_calling_a_hash;
    while(1) {
        cout << "\nSo, what would you like to enter? : ";
        cin >> i;
        list_that_the_code_was_calling_a_hash.push_back(i);
        if (list_that_the_code_was_calling_a_hash.size() > 10) {
            list_that_the_code_was_calling_a_hash.pop_front();
        }
        printhash(list_that_the_code_was_calling_a_hash);
    }
    return 0;
}

许多人会推荐使用std::vector,因为std::list 根本无法与您的CPU 缓存很好地交互。是的,std::vector 当你在容器中间弄脏东西时会做更多的复制。但它通过对缓存非常友好来弥补这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 2023-02-24
    • 2012-03-22
    • 2014-11-30
    • 2012-10-22
    • 1970-01-01
    相关资源
    最近更新 更多