具有指针和嵌套节点的树结构中的内存泄漏 class
memory leak in a Tree structure with pointers and Node nested class
我有这个任务,我必须在其中定义一个树 class,它有一个嵌套的私有节点 class。
问题是我既不能使用 smart pointers
也不能使用 std::vector
也不能使用复制运算符和复制赋值。我只能使用原始指针。
所以我写了 class 并用 valgrind 测试它以检查我是否有内存泄漏,我做到了。我知道内存泄漏可能来自节点 class,因为我没有 free
_children
但是当我这样做时,我遇到了分段错误。
我真的不知道如何解决这个问题。
在此先感谢您。
嗯,这不是清理代码,有很多内存泄漏的方法。
这是我认为您遇到问题的一个:
Tree() : _info(nullptr) {}
...
~Tree() {
delete _info;
}
当然,还有更多地方可能出错:
Tree& operator= (Tree&& t) // check _info not null before calling delete _info
换句话说,您的 class Tree
有两个构造函数。一个接受类型 T
的值:Tree(T data)
。这个构造器确实创建了一个 new
节点。这是您用来在 main()
函数中创建 Tree
的构造函数。
您还有另一个构造函数:默认构造函数。这是从 class Node
创建子节点时调用的构造函数:_children = new Tree<T,N>[N];
Tree() : _info(nullptr)
的默认构造函数不会创建新的 _info
节点。
当程序完成并调用析构函数时 - 尝试删除 nullptr
。
简单的解决方法是更改树析构函数:
~Tree() {
if (_info) delete _info;
}
你 Node::_children
是一个树数组,当你调用 getChildren
你 return 一个指针数组。所以调用 ins
可能会破坏内存。或者它可能会意外工作。
最好将 getChildren
更改为 return 对元素的引用,如下所示:
Tree& getChildren(int index) { return _children[index]; }
或将 _children
的 def 更改为 Tree** _children;
初始化为 _children = new Tree<T,N>*[N];
T** getChildren() { return &this->children; }
returns children
成员的地址。稍后,您可以通过数组索引通过取消引用来使用该地址,方法是:
_info->getChildren()[index] = childTree;
这正在调用未定义的行为。要解决这个问题:
将您的成员更改为:
Tree** _children;
将您的 ctor 更改为:
Node(T data)
: _data( std::move(data) )
, _children(new Tree<T,N>*[N]())
, _isWord(false)
{
}
并注意指针语法的数组以及元素的值初始化,这将对数组进行零填充。
最后,将 getChildren()
成员更改为:
Tree** getChildren() { return this->_children; }
这应该会减轻你的 UB,同时也减轻你的错。不想粉饰这个。此处的手动内存管理容易出错且存在问题。至少使用智能指针会好得多,如果不是在有保证的情况下使用完全具体的对象的话。但就是这样。
备选
完全失去动态 children
分配。它的大小已经由 N
的编译时规范确定。所以用那个。
成员变得简单:
Tree* _children[N];
ctor 仍然可以通过这样做进行值初始化:
Node(T data)
: _data( std::move(data) )
, _children()
, _isWord(false)
{
}
并从析构函数中完全删除 delete [] children;
;
我有这个任务,我必须在其中定义一个树 class,它有一个嵌套的私有节点 class。
问题是我既不能使用 smart pointers
也不能使用 std::vector
也不能使用复制运算符和复制赋值。我只能使用原始指针。
所以我写了 class 并用 valgrind 测试它以检查我是否有内存泄漏,我做到了。我知道内存泄漏可能来自节点 class,因为我没有 free
_children
但是当我这样做时,我遇到了分段错误。
我真的不知道如何解决这个问题。
在此先感谢您。
嗯,这不是清理代码,有很多内存泄漏的方法。 这是我认为您遇到问题的一个:
Tree() : _info(nullptr) {}
...
~Tree() {
delete _info;
}
当然,还有更多地方可能出错:
Tree& operator= (Tree&& t) // check _info not null before calling delete _info
换句话说,您的 class Tree
有两个构造函数。一个接受类型 T
的值:Tree(T data)
。这个构造器确实创建了一个 new
节点。这是您用来在 main()
函数中创建 Tree
的构造函数。
您还有另一个构造函数:默认构造函数。这是从 class Node
创建子节点时调用的构造函数:_children = new Tree<T,N>[N];
Tree() : _info(nullptr)
的默认构造函数不会创建新的 _info
节点。
当程序完成并调用析构函数时 - 尝试删除 nullptr
。
简单的解决方法是更改树析构函数:
~Tree() {
if (_info) delete _info;
}
你 Node::_children
是一个树数组,当你调用 getChildren
你 return 一个指针数组。所以调用 ins
可能会破坏内存。或者它可能会意外工作。
最好将 getChildren
更改为 return 对元素的引用,如下所示:
Tree& getChildren(int index) { return _children[index]; }
或将 _children
的 def 更改为 Tree** _children;
初始化为 _children = new Tree<T,N>*[N];
T** getChildren() { return &this->children; }
returns children
成员的地址。稍后,您可以通过数组索引通过取消引用来使用该地址,方法是:
_info->getChildren()[index] = childTree;
这正在调用未定义的行为。要解决这个问题:
将您的成员更改为:
Tree** _children;
将您的 ctor 更改为:
Node(T data)
: _data( std::move(data) )
, _children(new Tree<T,N>*[N]())
, _isWord(false)
{
}
并注意指针语法的数组以及元素的值初始化,这将对数组进行零填充。
最后,将 getChildren()
成员更改为:
Tree** getChildren() { return this->_children; }
这应该会减轻你的 UB,同时也减轻你的错。不想粉饰这个。此处的手动内存管理容易出错且存在问题。至少使用智能指针会好得多,如果不是在有保证的情况下使用完全具体的对象的话。但就是这样。
备选
完全失去动态 children
分配。它的大小已经由 N
的编译时规范确定。所以用那个。
成员变得简单:
Tree* _children[N];
ctor 仍然可以通过这样做进行值初始化:
Node(T data)
: _data( std::move(data) )
, _children()
, _isWord(false)
{
}
并从析构函数中完全删除 delete [] children;
;