【发布时间】:2021-05-04 17:47:39
【问题描述】:
我有一个关于如何解决我的 c++ 类中分配的内存泄漏的问题。在这个任务中,我应该实现一个 BST。我已经这样做了,而且功能方面,一切都按我的预期工作。但是,我的程序中有内存泄漏。唯一的问题是,当我尝试使用 Valgrind 调试我的内存泄漏时,我被指向我编译的代码中我没有编写的一行。在下面的函数中,我没有写这个函数,这是我的教授给我的,Valgrind指向下面函数中的NodeData* ptr = new NodeData(s);行:
void buildTree(BinTree& T, ifstream& infile) {
string s;
for (;;) {
infile >> s;
cout << s << ' ';
if (s == "$$") break; // at end of one line
if (infile.eof()) break; // no more lines of data
NodeData* ptr = new NodeData(s); // NodeData constructor takes string
// would do a setData if there were more than a string
bool success = T.insert(ptr);
if (!success)
delete ptr; // duplicate case, not inserted
}
}
在我的教授创建的 BST 驱动程序文件中,他正在分配 NodeData 指针。 NodeData 类也是教授给我的,但问题(我认为)是 NodeData 类的析构函数是空的。这是 NodeData 类的 .cpp 文件:
#include "nodedata.h"
//------------------- constructors/destructor -------------------------------
NodeData::NodeData() { data = ""; } // default
NodeData::~NodeData() { } // needed so strings are deleted properly
NodeData::NodeData(const NodeData& nd) { data = nd.data; } // copy
NodeData::NodeData(const string& s) { data = s; } // cast string to NodeData
//------------------------- operator= ----------------------------------------
NodeData& NodeData::operator=(const NodeData& rhs) {
if (this != &rhs) {
data = rhs.data;
}
return *this;
}
//------------------------- operator==,!= ------------------------------------
bool NodeData::operator==(const NodeData& rhs) const {
return data == rhs.data;
}
bool NodeData::operator!=(const NodeData& rhs) const {
return data != rhs.data;
}
//------------------------ operator<,>,<=,>= ---------------------------------
bool NodeData::operator<(const NodeData& rhs) const {
return data < rhs.data;
}
bool NodeData::operator>(const NodeData& rhs) const {
return data > rhs.data;
}
bool NodeData::operator<=(const NodeData& rhs) const {
return data <= rhs.data;
}
bool NodeData::operator>=(const NodeData& rhs) const {
return data >= rhs.data;
}
//------------------------------ setData -------------------------------------
// returns true if the data is set, false when bad data, i.e., is eof
bool NodeData::setData(istream& infile) {
getline(infile, data);
return !infile.eof(); // eof function is true when eof char is read
}
//-------------------------- operator<< --------------------------------------
ostream& operator<<(ostream& output, const NodeData& nd) {
output << nd.data;
return output;
}
它很短,但是你可以看到析构函数是空的。我不知道是否允许我编辑 NodeData 的析构函数来解决此内存泄漏问题,并且我不确定我的代码中的其他地方如何/在哪里可以删除这些指针(因为我也无法编辑驱动程序文件本身)。这不是我以前处理过的事情,也是我需要帮助的事情。有人可以帮我解决这个问题,并就我可以调查如何解决此内存泄漏的方法给我建议吗?
- 是否可以解决由于不删除指针所创建的指针导致的内存泄漏:
NodeData* ptr = new NodeData(s);在指针被传递到的函数中(插入函数。如果需要,我可以提供此代码)。 - 如果无法解决插入函数内部的内存泄漏,还有其他地方可以解决吗?我想我可能能够编辑 NodeData 类/制作析构函数,但我不完全确定如何在 NodeData 类中实现这样的析构函数。
由于它可能会有所帮助,这里是由 buildTree 函数引用的 BinTree 类的插入函数:
bool BinTree::insert(NodeData* nodeData){
if(root != nullptr){ //if the root of the tree is not NULL, meaning that the BST object exists, we call our insertHelp function.
insertHelp(this->root, nodeData);
return true;
}
else{ //If there is no root node created in our tree, we create a root node.
root = new Node;
root->left = nullptr;
root->right = nullptr;
root->data = nodeData;
return true;
}
return true;
}
// ---------------------------------insertHelp--------------------------------------------------
// Description: Private function that recursively calls itself in order to find the correct location in a BinTree object to add a new node.
// ---------------------------------------------------------------------------------------------------
bool BinTree::insertHelp(Node *nodePointer, NodeData* nodeData){
if(*nodeData < *nodePointer->data){ //Here we are deciding if we need to traverse left or not.
if(nodePointer->left != nullptr){ //If there is a node to the left, we call insertHelper again but on this node to the left.
insertHelp(nodePointer->left, nodeData);
}
else{ //if there is no node the left, and we need to go left, we create a new left node.
nodePointer->left = new Node;
nodePointer->left->left = nullptr;
nodePointer->left->right = nullptr;
nodePointer->left->data = nodeData;
return true;
}
}
else if(*nodeData > *nodePointer->data){//Here we are deciding if we need to traverse right or not.
if(nodePointer->right != nullptr){ //if there is no node the right, and we need to go left, we create a new left node.
insertHelp(nodePointer->right, nodeData);
}
else{ //if there is no node the right, and we need to go right, we create a new right node.
nodePointer->right = new Node;
nodePointer->right->left = nullptr;
nodePointer->right->right = nullptr;
nodePointer->right->data = nodeData;
return true;
}
}
else{ //catch all case. If something goes wrong, return false.
return false;
}
return true;
}
这是 BinTree 的当前析构函数:
BinTree::~BinTree(){
makeEmpty();
}
void BinTree::makeEmpty(){
//You make a tree empty by deleting all notes in a post-order traversal.
postOrderDeleteNode(this->root); //I will call my private postOrderDelete helper function. I broke things up this way to make the code cleaner.
}
// ---------------------------------postOrderDeleteNode--------------------------------------------------
// Description: This is the private function that performs a post-order traversal of the nodes in a BinTree
// and deletes each node by deallocating the memory assigned when each node is created.
// ---------------------------------------------------------------------------------------------------
void BinTree::postOrderDeleteNode(const Node *rootNode){
if(rootNode == nullptr){ //Base case, we have an empty tree at this node
return;
}
else{
postOrderDeleteNode(rootNode->left); //First we delete the left side of the tree
postOrderDeleteNode(rootNode->right); //Then we delete the right side of the tree.
delete rootNode; //We finally delete the root of the entire BST.
this->root = nullptr;
}
}
这里是 BinTree 的 .h 文件:
// ------------------------------------------------bintree.h-------------------------------------------------------
//
// Programmer Name: Aviv Weinstein
// Course Section Number: CSS 502 A
// Creation Date: 1/17/21
// Date of Last Modification: 1/27/21
// Instructor Name: Professor Dong Si
// --------------------------------------------------------------------------------------------------------------------
// Purpose -
// --------------------------------------------------------------------------------------------------------------------
// Notes on specifications, special algorithms, and assumptions:
// --------------------------------------------------------------------------------------------------------------------
#ifndef Bintree_H
#define Bintree_H
#include "nodedata.h"
//We have inlcuded iostream in nodedata.h
using namespace std;
class BinTree{
friend ostream& operator<<(ostream& out, const BinTree& T); //Used for output printing of BinTree objects. To display the tree using inorder traversal.
public:
BinTree(); // constructor
BinTree(const BinTree &); // copy constructor
~BinTree(); // destructor, calls makeEmpty
bool isEmpty() const; // true if tree is empty, otherwise false
void makeEmpty(); // make the tree empty so isEmpty returns true
BinTree& operator=(const BinTree &); //The assignment operator (=) to assign one tree to another
bool operator==(const BinTree &) const; //Boolean comparison operator for equal
bool operator!=(const BinTree &) const; //Boolean comparison operator for NOT equal
bool insert(NodeData*); //inserts a new node, with NodeData, into the BinTree object.
bool retrieve(const NodeData &, NodeData* &) const; //Looks for a specific node in the BinTree. Returns true if the node exists.
void displaySideways() const; // provided below, displays the tree sideways
int getHeight(const NodeData &) const; //function to find the height of a given value in the tree.
void bstreeToArray(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
void arrayToBSTree(NodeData* []); //function to fill an array of Nodedata* by using an inorder traversal of the tree
private:
struct Node {
NodeData* data; // pointer to data object
Node* left; // left subtree pointer
Node* right; // right subtree pointer
};
Node* root; // root of the tree
//Utility functions
void sideways(Node*, int) const; //provided below, helper for displaySideways()
bool insertHelp(Node *nodePointer, NodeData* nodeData); //Helper function for the insert function.
void postOrderDeleteNode(const Node *node); // Helper function. Deletes all nodes in a BinTree object
void inorderHelper(Node *startNode) const; //Helper function for printing out all nodes in a BST using in-order traversal
void inorderHelperArray(NodeData* a[], Node *startNode) const; //Helper function for the operator == and operator!= functions.
//Used to compare BSTs to each other using in-order traversal.
Node* retrieveHelper(Node *root, const NodeData &nodeData) const; //A hlper function for retriving a node in a BinTree
int getHeightUtil(Node *node)const; //Helper for performing a recursive calculation of the height of a node in a BinTree.
void preorderTraversal(Node* node); //Performs a pre-order traversal of a BinTree object. Called by the operator= function.
void convert(NodeData* a[], int start, int end, Node *root); //Performs a utility function in the arraytoBSTree function.
//Selects the correct array indexes to be added next into an array.
};
#endif
【问题讨论】:
-
当
insertHelp递归调用自身时,您不会检查返回值。即使insertHelp失败,BinTree::insert也将始终返回true。 -
~NodeData中不需要任何内容,因为该类不分配任何内存。 (顺便说一句,也不需要opertator=、复制构造函数或默认构造函数——它们的作用与自动生成的构造函数相同。)我认为问题出在您的代码中。删除添加到其中的节点是BinTree的责任,但未显示。 -
@JasperKent,这就是我感到困惑的地方。如何让 BinTree 删除 NodeData?如果有帮助,我可以添加 BinTree 的析构函数。
-
编译您的 C++ 代码,将 GCC 调用为
g++ -Wall -Wextra -g,然后使用 valgrind。也许也可以使用its address sanitizer。顺便说一句,考虑使用std::set 或至少阅读red-black trees 和Introduction to algorithms -
无关:我建议你将
void buildTree(BinTree& T, ifstream& infile)重命名为std::istream& operator>>(std::istream&, BinTree&)instread。然后,您可以使用常见的instream >> bintree_object从任何 类型的istream填充您的bintree。
标签: c++ memory-leaks destructor dynamic-memory-allocation