【问题标题】:Query related to memory management in C++C++中与内存管理相关的查询
【发布时间】:2013-02-17 07:01:03
【问题描述】:

您好,我有一个关于 C++ 内存管理的一般查询。 只有在这个程序的帮助下,我才知道 new 用于在堆上分配内存,临时变量在堆栈上分配内存。如果我们在堆上分配内存,我们也必须手动释放它,否则会出现内存泄漏.

但是在程序中,我通过在堆上创建一个 BST 类型的新变量 temp 来更新名为 Insert 的函数中的 BST 结构对象。但是我不确定如何释放该内存。如果我使用 free 命令在函数结束时,即 free(temp) 然后存储在该内存中的值将丢失,如果我再次尝试访问它,我会得到一个错误,我当然不能在 main 中使用 free(temp),因为它不是main 的局部变量。 谁能告诉我应该怎么做。

顺便说一句,我必须提到,如果不使用 free(temp),我的程序也可以正常工作,但是我猜内存泄漏正在发生,这很糟糕。

我也有点困惑,如果我评论析构函数~BST(),但当我取消注释时给出链接器错误,为什么我的程序运行没有错误。

#include<iostream>
#include<string>
#include<conio.h>
#include<array>
#include<stack>
#include<sstream>
#include<algorithm>
#include<vector>
#include<ctype.h>//isdigit
#include<deque>
#include<queue>
#include<map>
using namespace::std;
struct BST
{
    int data;
    BST *left;
    BST *right;
    BST(int d,struct BST* l,BST *r):data(d) , left(l) ,right(r)
    {
    }

    BST()
    {
    }
    //~BST();
};

void levelOrder(struct BST *root)
{
    struct BST *temp=NULL;
    int count =0;
    deque<struct BST*> dq;
    if(!root)
    {
        return;
    }
    dq.push_back(root);
    count=dq.size();
    while(!dq.empty())
    {
        temp=dq.front();
        cout<<temp->data<<" ";
        if(temp->left)
        {
            dq.push_back(temp->left);
        }
        if(temp->right)
        {
            dq.push_back(temp->right);
        }
        dq.pop_front();
        if(--count==0)
        {
            cout<<endl;
            count=dq.size();
        }
    }
}
void Insert(struct BST*root,int data)
{
    //struct BST temp(data,NULL,NULL);
    BST *temp = new BST(data,NULL,NULL);
    temp->data =data;
    temp->left= NULL;
    temp->right=NULL;
    if(!root)
    {
        return;
    }
    while(root)
    {
        if((root)->data >data)
        {
            (root)=(root)->left;
            if(!(root)->left)
            {
                (root)->left=temp;
                break;
            }
        }
        else
        {
            (root)=(root)->right;
            if(!(root)->right)
            {
                (root)->right=temp;
                break;
            }
        }
    }
}
int main()
{
    deque<struct BST> dq1,dq2;
    BST e(4,NULL,NULL);
    BST f(3,NULL,NULL);
    BST d(1,&f,NULL);
    BST b(2,&d,&e);
    BST c(8,NULL,NULL);
    BST a(6,&b,&c);

    levelOrder(&a);
    Insert(&a,5);
    cout<<a.left->right->right->data;
    cout<<endl;
    levelOrder(&a);
    _getch();
    return 0;
}

【问题讨论】:

  • 这比惯用的 C++ 更像 C-in-C++。您的 struct BST 应该是一个类,并且应该将 Insert 作为成员函数。上个世纪的裸指针太棒了。如果您使用类似于std::unique_ptr 和/或std::shared_ptr 的东西,您的内存泄漏问题就会消失。

标签: c++ pointers free new-operator destructor


【解决方案1】:

在您的 Insert 方法中创建的 BST *temp 是您插入的新节点/子树,在整个树被销毁或节点以某种方式删除之前,您不希望 deleteDelete 你还没有写的函数。

关于你的最后一点:

在没有析构函数的情况下运行这个特定程序会内存泄漏,但不会访问任何无效的内存段,这就是它运行时没有任何错误的原因。

当您在代码中取消注释析构函数声明时,您会收到链接器错误,因为您没有定义析构函数,您只是告诉编译器/链接器有应该是一个析构函数,但没有。即使你只想要一个空的,它也必须是~BST() {}

【讨论】:

  • 如果我定义像 ~BST(){free(this)} 这样的析构函数会怎样,即使我在程序中的任何地方声明像 struct BST *temp = new BST(); 这样的东西,这是否会确保不会发生内存泄漏
  • 不,freeing this 不是正确的方法(另外,free 不是好的 C++)。当调用析构函数时,this 已经被清理了。析构函数必须负责清理子节点!
  • 那么你能告诉我我应该怎么做吗?在我的主程序中将无法访问新节点,但是我不知道之后如何释放内存
【解决方案2】:

首先,在 C++ 中,您通常应该使用newdelete(它们称为ctors/dtors 等)对于数组,使用delete[]new/deletemalloc/free 不兼容。

我猜 BST 是一个二叉搜索树。所以你有一棵动态分配的内存树。

您必须释放整棵树,这意味着您也应该按顺序执行,以免出现悬空指针。

可以通过确保 BST 节点始终释放其子节点来显着降低复杂性。那么当你删除根节点时,它会递归地删除所有其他节点。

在我看来,最简单的方法是使用智能指针,例如 shared_ptr&lt;T&gt;unique_ptr&lt;T&gt;auto_ptr(最后一个有警告,但我不会在这里解决它们。)

结构将如下所示:

struct BST
{
  /* ctor, dtor omitted for brevity. */

  std::unique_ptr<BST> left;
  std::unique_ptr<BST> right;
}

您的 BST 节点超出范围,即您 delete 它,或者它被分配在堆栈上并且代码退出块。 left 和 right 的析构函数被调用,unique_ptr 实现确保在它存储的指针上调用delete

【讨论】:

    【解决方案3】:

    两个构造函数都应该为左右成员分配默认值,至少为NULL; 不应为它们分配类之外的值。将默认参数添加到构造函数。为避免泄漏,您不应该在需要之前创建对象。或者有一个标志,最初为 false,如果您使用过它,则将其设置为 true。如果标志仍然为假,则在返回时删除。

    【讨论】:

      【解决方案4】:

      首先,您应该在 C++ 中使用 newdelete 进行内存管理,而不是 malloc()free()

      话虽如此,请注意您分配了另一个指针leftright 来指向最初分配给变量temp 的内存。您的树将允许您访问分配的内存,尽管通过原始 temp 变量之外的其他变量。这意味着您可以在BST 类中使用这些变量来delete 内存。通常这是在析构函数中完成的。

      请注意,您在这里管理的是内存,而不是变量。让我们用一个简单的例子来看看区别:

      int main() {
          int* intPtr = new int;
          int* temp = intPtr;
      
          delete temp;
          temp = NULL;
      }
      

      如您所见,这段代码分配了一块内存来存储int。该内存有两个指向它的指针。您可以使用任一指针删除内存,只要您不使用两者都删除即可。当您了解内存管理时,这绝对是一种平衡行为。您必须确保所有内存都被释放,同时永远不要尝试释放相同的内存块两次。

      【讨论】:

        猜你喜欢
        • 2018-10-14
        • 1970-01-01
        • 2011-07-08
        • 2013-07-26
        • 2011-09-30
        • 1970-01-01
        • 2011-03-25
        • 2012-01-24
        • 2011-01-21
        相关资源
        最近更新 更多