2

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

但是在程序中,我通过在堆上创建一个 BST 类型的新变量 temp 来更新名为 Insert 的函数中的 BST 结构对象。但是我不确定如何释放该内存。如果我在最后使用 free 命令如果我尝试再次访问它,我肯定不能在 main 中使用 free(temp),因为它不是局部变量,所以存储在该内存中的值将会丢失主要。有人可以告诉我应该做什么。

顺便说一句,我必须提到,在不使用 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;
}
4

4 回答 4

1

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

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

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

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

在我看来,最简单的方法是使用智能指针,如shared_ptr<T>, unique_ptr<T>or 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它存储的指针。

于 2013-03-02T23:06:59.990 回答
0

BST *temp在您的方法中创建的是您插入的新节点/子树,在整个树被破坏或节点在您尚未编写的某种函数中被删除之前Insert,您不希望它。deleteDelete

关于你的最后一点:

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

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

于 2013-03-02T22:59:22.520 回答
0

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

于 2013-03-02T23:08:52.050 回答
0

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

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

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

int main() {
    int* intPtr = new int;
    int* temp = intPtr;

    delete temp;
    temp = NULL;
}

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

于 2013-03-02T23:09:17.597 回答