【发布时间】:2016-09-19 19:37:37
【问题描述】:
假设以下类声明:
class NTree
{
private:
const T* fKey;
NTree<T, N>* fNodes[N]; // N subtrees of degree N
NTree();
...
}
我们可以在其中添加一些 fNode,表示给定索引的子树。这些将使用new 动态分配。但是,有些元素是静态的,而不是动态分配的:
public:
static NTree<T, N> NIL; // sentinel
...
我们选择使用上面提供的默认构造函数在堆栈上分配它。
template<class T, int N>
NTree<T, N> NTree<T, N>::NIL;
现在,假设我们希望删除一个 NTree。 NTree 类是递归的,其中有指向 NTree 的指针。 这就是我正在努力解决的问题。
我理解析构函数背后的逻辑,如果我们有例如
class MyClass
{
private:
TypeA * myA;
TypeB * myB;
TypeC * myC;
...
}
我们可以使用析构函数来防止这些指针悬空或丢失。
~MyClass()
{
delete myA;
delete myB;
delete myC;
}
但是,当涉及到递归类时,我不知道如何理解这一点,如何理解删除。
一个简单的想法:
template<class T, int N>
NTree<T, N>::~NTree()
{
delete[] fNodes;
}
但是,它不起作用,因为某些节点是 NIL(堆栈分配),因此删除它们会导致崩溃。
另一个想法是:
template<class T, int N>
NTree<T, N>::~NTree()
{
for (int i = 0; i < N; i++)
{
delete fNodes[i];
}
}
但是,这将导致堆栈溢出,因为每次递归调用~NTree(),堆栈都会被帧轰炸
还有以下内容:
template<class T, int N>
NTree<T, N>::~NTree()
{
for (int i = 0; i < N; i++)
{
if (fNodes[i] != &NIL)
delete fNodes[i];
}
}
导致读取异常,因为递归调用将为特定堆栈帧释放 fNodes[i],因此尝试访问该内存是无效的。
所以我的问题是,我怎样才能删除一个成员变量,该成员被递归地定义为同一个类?
如何让我的析构函数工作?
编辑:尝试提供更多信息,但不会过于复杂
我正在定义一个析构函数,因此向您展示我的复制构造函数和赋值运算符可能是明智之举:
template<class T, int N>
NTree<T, N> & NTree<T, N>::operator=(const NTree & aOtherNTree)
{
//This is an already initialized object.
if (this != &aOtherNTree)
{
fKey = aOtherNTree.fKey;
for (int i = 0; i < N; i++)
{
if (fNodes[i] == &NIL)
continue; //continue if nil
delete fNodes[i]; //important -- so no dangling pointer
fNodes[i] = new NTree<T, N>; //allocate memory
fNodes[i] = aOtherNTree.fNodes[i]; //assign
}
}
return *this;
}
..
template<class T, int N>
NTree<T, N>::NTree(const NTree & aOtherNTree)
{
//This is a new object, nothing is initalized yet.
fKey = aOtherNTree.fKey;
for (int i = 0; i < N; i++)
{
if (fNodes[i] == &NIL)
continue;
fNodes[i] = new NTree<T, N>;
fNodes[i] = aOtherNTree.fNodes[i];
}
}
我希望这显示了我分配内存时需要在析构函数中显式删除的所有实例。 NIL 是一个哨兵,我们总是为 NIL 分配一个叶子。
这部分是教授提供的,是我们设置初始对象的地方:
NS3Tree root(A);
root.attachNTree(0, *(new NS3Tree(A1)));
root.attachNTree(1, *(new NS3Tree(A2)));
root.attachNTree(2, *(new NS3Tree(A3)));
root[0].attachNTree(0, *(new NS3Tree(AA1)));
root[1].attachNTree(0, *(new NS3Tree(AB1)));
root[1].attachNTree(1, *(new NS3Tree(AB2)));
A1、A2等都是字符串
【问题讨论】:
-
delete[] fNodesfNodes没有被分配到new,所以你没有业务到delete它。您可能想要delete数组的单个元素,假设 那些 已通过new获得。 -
@IgorTandetnik 啊,是的。你说得对。在这种情况下,删除 fNodes 肯定是行不通的。但是,我展示了两种尝试删除 fNodes 元素的方法,但没有成功。
-
深度优先遍历树(使用显式堆栈,而不是递归调用)。自下而上删除节点。
-
@IgorTandetnik 好的,我会试试的。但是我预计的问题是在析构函数中使用
delete会自动强制递归? -
如果您删除的节点是叶节点并且没有任何自己的子节点,则不会。
标签: c++ pointers recursion tree