[英]Understanding clang-tidy "DeadStores" and "NewDeleteLeaks" warnings
I am relatively new to c++ programming, especially when dealing with OOP and pointers.我对 c++ 编程比较陌生,尤其是在处理 OOP 和指针时。
I am trying to implement a binary search tree, and here is the code I have我正在尝试实现二叉搜索树,这是我拥有的代码
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
}
Now, I have two questions.现在,我有两个问题。
I realised that the L52 labelled above would not work.我意识到上面标记的 L52 不起作用。 For example, if I do
TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node);
例如,如果我执行
TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node);
TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node);
, it would not work. ,它不会工作。 I am not sure how to fix this, so any help would be great.
我不知道如何解决这个问题,所以任何帮助都会很棒。
Running clang-tidy on my code, I get the following slightly confusing warning:在我的代码上运行 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)); }
^
I am not sure what this means, and whether it would be dangerous in any way.我不确定这意味着什么,以及它是否会以任何方式危险。 I found this from the LLVM docs and it seems that the temporary
new TreeNode(v)
is causing the problem.我从 LLVM 文档中找到了这个,似乎临时的
new TreeNode(v)
导致了这个问题。 What is a better way to achieve what I want?有什么更好的方法来实现我想要的? Any further explanation or resources that I can read would be great.
我可以阅读的任何进一步的解释或资源都会很棒。
Of course, any improvement to my bad coding style would be great as well ;) formatter saves the day.当然,对我糟糕的编码风格的任何改进都会很棒;)格式化程序可以节省一天的时间。
Thank you!谢谢!
The main problem with the fragment:片段的主要问题:
void insert(TreeNode* root, TreeNode* v) {
// inserts v into root, assuming values distinct
if (root == NULL) {
root = v; // L52
return;
}
is, although you have set a new value for root
, root
is a parameter to the insert
function, and consequently is discarded when insert
returns.也就是说,尽管您为
root
设置了新值,但root
是insert
函数的参数,因此在insert
返回时被丢弃。
One way to fix this is to return the new value:解决此问题的一种方法是返回新值:
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
}
Every place insert
returns (including at the end), return the value of root
.每个地方
insert
返回(包括在末尾),返回root
的值。
Then when you call insert
, save the returned value:然后当你调用
insert
时,保存返回值:
TreeNode* root = ...; // some existing tree
root = insert(root, 5); // insert 5 and save the result
Now, the new value of root
points to the tree that contains the inserted value.现在,
root
的新值指向包含插入值的树。
Invoking new
allocates memory.调用
new
分配内存。 It must eventually be freed, otherwise (if this keeps happening) your program will eventually use all available memory.它最终必须被释放,否则(如果这种情况继续发生)您的程序最终将使用所有可用内存。 See the question When should I use the new keyword in C++?
请参阅问题何时应该在 C++ 中使用 new 关键字? .
. Note especially this advice: "every time you type
new
, type delete
."请特别注意以下建议:“每次键入
new
时,键入delete
。”
Now, in this code as it stands, the problem is actually not the missing delete
, it is the failure to return the updated root
.现在,在这段代码中,问题实际上不是缺少
delete
,而是未能返回更新后的root
。 clang-tidy
realizes that, because the updated root
is never returned, you can't possibly eventually delete
it, so complains. clang-tidy
意识到,因为更新的root
永远不会返回,你不可能最终delete
它,所以抱怨。 Once you start returning that value, clang-tidy
will wait to see what you do next.一旦你开始返回那个值,
clang-tidy
就会等着看你下一步做什么。
As for what to do next, one simple approach is to regard TreeNode
as owning its children, meaning it is obliged to delete
them when it is destroyed.至于接下来要做什么,一种简单的方法是将
TreeNode
视为拥有其子节点,这意味着它必须在销毁时将其delete
。 For example, first add a destructor method to TreeNode
:例如,首先给
TreeNode
添加一个析构方法:
struct TreeNode {
...
~TreeNode() { // <--- this is the destructor
delete left; // free left child (if not NULL)
delete right; // free right child
}
};
Then in your code that manipulates trees, remember to delete
the root node when finished:然后在操作树的代码中,记得在完成后
delete
根节点:
TreeNode* root = NULL;
root = insert(root, 1);
root = insert(root, 2);
delete root; // <--- free entire tree
My suggestions above use a very direct, literal style of memory management, with explicit use of new
and delete
where needed.我上面的建议使用了一种非常直接的、字面意义上的内存管理方式,在需要的地方显式地使用了
new
和delete
。 However, it is easy to forget to use delete
, and tricky to get it right in the presence of exceptions.但是,很容易忘记使用
delete
,并且在出现异常时很难正确使用它。 A commonly preferred alternative is to use smart pointers .通常首选的替代方法是使用智能指针。 But that's a somewhat advanced topic, so when just starting out, I recommend first getting comfortable with explicit
new
and delete
, but keep in mind there is a better way when you're ready for it.但这是一个有点高级的话题,所以刚开始时,我建议先熟悉显式
new
和delete
,但请记住,当你准备好时,还有更好的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.