我必须手动解构所有对象吗
Posted
技术标签:
【中文标题】我必须手动解构所有对象吗【英文标题】:do i have to manually deconstruct all objects 【发布时间】:2016-06-09 20:40:19 【问题描述】:我创建了一个名为 BST 的类,它有一个成员根。我知道当我调用 BST 类的解构函数时,它会删除根目录并释放该类占用的内存。
我想知道,解构器是否会解构所有与 BST 对象关联的节点。即 root 的左孩子和右孩子以及他们的左孩子和右孩子等等。
我的猜测是没有。在这种情况下,我认为我将不得不进行后序遍历并手动删除每个节点。有没有办法一次完成。无需在树节点周围走动
#ifndef BST_H
#define BST_H
#include "bst.h"
#include <stdio.h>
#include<iostream>
class bst
protected:
struct node
node* p;
node* lc;
node* rc;
int key;
node(int x,node* p=nullptr,node* lc=nullptr,node* rc=nullptr):key(x)
~node()
std::cout<<"decontrucotr node";
;
node* nil=new node(0);
node* root;
public:
bst();
virtual ~bst();
void put(int x);
void in_order(node* x);
void in_order();
void pre_order(node* x);
void pre_order();
private:
;
#endif // BST_H
函数在此定义
#include "bst.h"
#include <stdio.h>
#include<iostream>
bst::bst():root(nil)
bst::~bst()
std::cout<<"deconstructor tree"<<'\n';
void bst::put(int x)
node* k=new node(x,this->nil,this->nil,this->nil);
node* y=this->root;
node* p=y;
while (y != nil)
p=y;
if (y->key>x)
y=y->lc;
else
y=y->rc;
if (p==nil)
this->root=k;
k->lc=this->nil;
k->rc=this->nil;
k->p=this->nil;
else if(p->key>x)
p->lc=k;
p->lc->p=p;
k=p->lc;
k->lc=this->nil;
k->rc=this->nil;
else
p->rc=k;
p->rc->p=p;
k=p->rc;
k->lc=this->nil;
k->rc=this->nil;
void bst::in_order(node* x)
if(x != nil)
this->in_order(x->lc);
printf("%d%c",x->key,'\t');
this->in_order(x->rc);
void bst :: in_order()
this->in_order(this->root);
printf("%c",'\n');
void bst::pre_order(node* x)
if(x!=this->nil)
printf("%d%c",x->key,'\t');
pre_order(x->lc);
pre_order(x->rc);
void bst::pre_order()
pre_order(this->root);
printf("%c",'\n');
【问题讨论】:
快速提问:您研究过 STL 的智能指针吗? ***.com/questions/106508/… en.cppreference.com/w/cpp/memory/shared_ptr 如果您需要使用原始指针或不能使用 C++11 或更高版本,我深表歉意,但如果不是,请查看智能指针.内存管理可以是一个真正的熊。 @Wikiti 我认为你的意思是每个new
都应该有一个delete
。可能听起来像吹毛求疵,但有时言语很重要。它既不称为“破坏”也不称为“解构”,但必须删除新对象(这包括调用析构函数,但这还不是全部)。
@Ian:不,每次你有新东西时,都用std::unique_ptr
替换它。
@WilliamKappler new
和 delete
比托管指针更受欢迎的例子有哪些?我能想到的只是我将数据返回到显然不能使用托管指针的 C 代码的情况。即使那样,我也会使用unique_ptr
,直到我准备好释放并返回指针。
我在 20 年前学习了 C++。我对 new 和 delete 很满意。
【参考方案1】:
我创建了一个名为 BST 的类,它有一个成员根。我知道当我调用 BST 类的解构函数时,它会删除根目录并释放该类占用的内存。
在您展示的实现中,~bst
析构函数本质上是空的(日志不计算在内),它根本没有释放任何节点。
我想知道,解构器是否会解构所有与 BST 对象关联的节点。
bst
对象本身已被破坏。它的子节点不是,不是。
即 root 的左孩子和右孩子以及他们的左孩子和右孩子等等。
不在当前实现中,不。您需要编写该逻辑。
在这种情况下,我认为我将不得不进行后序遍历并手动删除每个节点。
在当前的实现中,是的。
有什么方法可以一次性完成。无需在树节点周围走动
要么:
编码~node
析构函数以删除其直接子节点。然后~bst
析构函数可以删除它的root
节点,它下面的所有其他节点都会被递归删除。
使用智能指针,例如std::unique_ptr
,而不是原始指针。让编译器为您完成所有工作。
【讨论】:
拥抱现代 c++!【参考方案2】:“我必须手动解构所有对象吗?”
是的,因为 C/C++ 不跟踪内存,您告诉它您将自行管理,这就是使用 new
和/或 malloc
隐含的意思。
如果您愿意,thereareoptions 可以用于不同的用例。但是,除此之外,您必须始终将每个分配 (new
) 与恰好 1 个解除分配 (delete
) 配对。
“我想知道,解构器会解构所有与 BST 对象相关的节点吗?”
不,因为你没有告诉它。
你应该:
~node()
std::cout<<"decontrucotr node";
delete lc;
delete rc;
也就是说,bst::put(int x)
对我来说毫无意义,我发现其中很可能存在类似的错误。您应该努力编写文档化、清晰的代码——尤其是当您打算向其他人寻求帮助时。
【讨论】:
你所说的创建了一个无限递归 @Nikhil Infinite recursion 在删除操作中应该是不可能的。如果你让一个孩子成为自己的父母(甚至间接地),当你尝试delete
一个对象两次时,我预计会崩溃,至少如果你将它构建为调试。也许没有调试就可以发生无限递归;这将是未定义的行为。如果它只是一直运行而不会崩溃,我建议您添加一些调试功能以将节点结构打印到控制台,如果原因不明显,请提出单独的问题。
当我删除 cout 时它崩溃了。但是使用 cout 它只是继续打印用解构函数编写的字符串。我做了一个哨兵对象 nil 是因为那个吗? nil 就在那里,所以当我从这个类继承到红黑树时
@Nikhil 实际上,我没有意识到这一点。为什么要使用哨兵对象代替 null?使用 null 或 nullptr 表示空指针。 delete
在 null 上是无操作的。对象上的delete
,即使您称其为“nil”,也会删除该对象。所以,现在我看着它,递归地删除nil
。至于打印不会使其崩溃......未定义的行为。不知道为什么会修复崩溃,但打印到控制台本身并没有真正影响这一点。
是的,没有 nil 它会起作用,我首先尝试过,但对于红黑树,我需要 nil。 nil sentinel 使编码更容易,它只是一个帮助工具。我一直在寻找一种方法,即使在 nil 的情况下也能正常工作。以上是关于我必须手动解构所有对象吗的主要内容,如果未能解决你的问题,请参考以下文章