[英]Understanding clang-tidy "DeadStores" and "NewDeleteLeaks" warnings
我对 c++ 编程比较陌生,尤其是在处理 OOP 和指针时。
我正在尝试实现二叉搜索树,这是我拥有的代码
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(NULL), right(NULL){};
TreeNode(int v) : val(v), left(NULL), right(NULL){};
TreeNode(int v, TreeNode* l, TreeNode* r) : val(v), left(l), right(r){};
};
void insert(TreeNode* root, TreeNode* v) {
// inserts v into root, assuming values distinct
if (root == NULL) {
root = v; // L52
return;
}
if (v == NULL) {
return;
}
// traverse through tree using structure of BST
TreeNode* cur = root;
while (cur != NULL) {
if (cur->val <= v->val) {
if (cur->right == NULL) {
cur->right = new TreeNode(v->val);
break;
}
cur = cur->right;
} else if (cur->val >= v->val) {
if (cur->left == NULL) {
cur->left = new TreeNode(v->val);
break;
}
cur = cur->left;
}
}
}
void insert(TreeNode* root, int v) {
insert(root, new TreeNode(v)); // L78
}
现在,我有两个问题。
我意识到上面标记的 L52 不起作用。 例如,如果我执行TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node);
TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node);
,它不会工作。 我不知道如何解决这个问题,所以任何帮助都会很棒。
在我的代码上运行 clang-tidy,我得到以下稍微令人困惑的警告:
./main.cpp:78:69: warning: Potential memory leak [clang-analyzer-cplusplus.NewDeleteLeaks]
void insert(TreeNode* root, int v) { insert(root, new TreeNode(v)); }
^
./main.cpp:132:3: note: Calling 'insert'
insert(node, 3);
^
./main.cpp:78:51: note: Memory is allocated
void insert(TreeNode* root, int v) { insert(root, new TreeNode(v)); }
^
./main.cpp:78:69: note: Potential memory leak
void insert(TreeNode* root, int v) { insert(root, new TreeNode(v)); }
^
我不确定这意味着什么,以及它是否会以任何方式危险。 我从 LLVM 文档中找到了这个,似乎临时的new TreeNode(v)
导致了这个问题。 有什么更好的方法来实现我想要的? 我可以阅读的任何进一步的解释或资源都会很棒。
当然,对我糟糕的编码风格的任何改进都会很棒;)格式化程序可以节省一天的时间。
谢谢!
片段的主要问题:
void insert(TreeNode* root, TreeNode* v) {
// inserts v into root, assuming values distinct
if (root == NULL) {
root = v; // L52
return;
}
也就是说,尽管您为root
设置了新值,但root
是insert
函数的参数,因此在insert
返回时被丢弃。
解决此问题的一种方法是返回新值:
TreeNode* // <-- changed return type
insert(TreeNode* root, TreeNode* v) {
// inserts v into root, assuming values distinct
if (root == NULL) {
root = v; // L52
return root; // <---- changed to return a value
}
每个地方insert
返回(包括在末尾),返回root
的值。
然后当你调用insert
时,保存返回值:
TreeNode* root = ...; // some existing tree
root = insert(root, 5); // insert 5 and save the result
现在, root
的新值指向包含插入值的树。
调用new
分配内存。 它最终必须被释放,否则(如果这种情况继续发生)您的程序最终将使用所有可用内存。 请参阅问题何时应该在 C++ 中使用 new 关键字? . 请特别注意以下建议:“每次键入new
时,键入delete
。”
现在,在这段代码中,问题实际上不是缺少delete
,而是未能返回更新后的root
。 clang-tidy
意识到,因为更新的root
永远不会返回,你不可能最终delete
它,所以抱怨。 一旦你开始返回那个值, clang-tidy
就会等着看你下一步做什么。
至于接下来要做什么,一种简单的方法是将TreeNode
视为拥有其子节点,这意味着它必须在销毁时将其delete
。 例如,首先给TreeNode
添加一个析构方法:
struct TreeNode {
...
~TreeNode() { // <--- this is the destructor
delete left; // free left child (if not NULL)
delete right; // free right child
}
};
然后在操作树的代码中,记得在完成后delete
根节点:
TreeNode* root = NULL;
root = insert(root, 1);
root = insert(root, 2);
delete root; // <--- free entire tree
我上面的建议使用了一种非常直接的、字面意义上的内存管理方式,在需要的地方显式地使用了new
和delete
。 但是,很容易忘记使用delete
,并且在出现异常时很难正确使用它。 通常首选的替代方法是使用智能指针。 但这是一个有点高级的话题,所以刚开始时,我建议先熟悉显式new
和delete
,但请记住,当你准备好时,还有更好的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.