这个极客对极客的trie实现是否存在内存泄漏问题

Is this geeksforgeeks trie implementation having a memory leak problem?

本文关键字:存在 内存 泄漏 是否 问题 trie 实现      更新时间:2023-10-16

我正在阅读一个geeksfoekees_implementation_of_tree,我发现new创建的TrieNode对象被取消删除,在主函数中,有一个取消删除的指针root:

struct TrieNode *root = getNode(); 

在函数getNode()中还有未删除的指针pNode,是否存在内存泄漏?是否应该有一个函数负责销毁由指针组成的trie树?

struct TrieNode *getNode(void) 
{ 
struct TrieNode *pNode =  new TrieNode; 
pNode->isEndOfWord = false; 

for (int i = 0; i < ALPHABET_SIZE; i++) 
pNode->children[i] = NULL; 

return pNode; 
} 

此函数表示一个约定,其中函数的调用方负责删除分配的TrieNode。只有当调用方不遵守此合约时,内存才会泄漏。

由于您说TrieNode没有在main中的任何位置删除,因此可能存在泄漏。除非你能找到这个结构被删除的地方,否则就存在漏洞。这就是为什么RAII是一个如此强大的概念。如果有一个包含所有TrieNodeTrie对象,并负责节点分配和删除,那么您根本不必担心泄漏。

让调用者负责管理分配的资源是危险的。不要这么做。

如果程序足够简单,它所做的只是获取TrieNode,用它们做一些事情,然后退出,那么您可以争辩说,这个特定的实现不一定是泄漏。在这种情况下,内存将在程序退出时释放给操作系统。但这是一个语义争论,提供这样做的示例代码是一种糟糕的做法,可能会导致狂热的程序员做出这种糟糕的做法。

泄漏或不泄漏

根据维基百科的说法,当计算机程序错误地管理内存分配,不再需要的内存没有被释放时,就会发生泄漏:

  • getNode()的作用是返回一个指向初始化的TrieNode的指针。因此,它本身不会泄漏任何内容:我们可以假设,根据它的用途,当函数返回指针时,仍然需要相应的对象。

  • 调用上下文确实会泄漏内存:正如您正确指出的,它创建了一个具有getNode()的节点,并且从不删除该节点。这是不好的做法。

但您必须在其上下文中替换此代码:本文是一篇教程。它建议将删除作为一项额外的工作。

为什么它们在教程中不显示删除

因为删除TrieNode不是一件容易的事,如果你必须自己小心:删除一个节点还需要找到所有应该删除的引用节点。由于trie是一个图,因此多个节点可以指向同一个目的地。因此,如果你不想因为悬挂指针和双重删除而冒UB的风险,你就不能盲目删除这些节点。你需要实现标记算法或某种参考计数

但不要遵循本教程

这个TrieNode实现非常糟糕:在现代C++中:

  • 您可以让构造函数初始化TrieNode,无论它是动态创建的节点还是本地节点
  • 您应该使用智能指针而不是原始指针。由于一个节点本质上可以被引用多次,因此您应该选择shared_ptr
  • 您可能更喜欢使用CCD_ 19甚至CCD_

但我还是让你把删除作为练习;-(