繁体   English   中英

了解铿锵有力的“DeadStores”和“NewDeleteLeaks”警告

[英]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
}

现在,我有两个问题。

  1. 我意识到上面标记的 L52 不起作用。 例如,如果我执行TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node); TreeNode* node = new TreeNode(5); TreeNode* empty = NULL; insert(empty, node); ,它不会工作。 我不知道如何解决这个问题,所以任何帮助都会很棒。

  2. 在我的代码上运行 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)导致了这个问题。 有什么更好的方法来实现我想要的? 我可以阅读的任何进一步的解释或资源都会很棒。

当然,对我糟糕的编码风格的任何改进都会很棒;)格式化程序可以节省一天的时间。

谢谢!

Q1:如何插入二叉树?

片段的主要问题:

void insert(TreeNode* root, TreeNode* v) {
  // inserts v into root, assuming values distinct
  if (root == NULL) {
    root = v; // L52
    return;
  }

也就是说,尽管您为root设置了新值,但rootinsert函数的参数,因此在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的新值指向包含插入值的树。

Q2:clang-tidy 在抱怨什么?

调用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

关于智能指针的说明

我上面的建议使用了一种非常直接的、字面意义上的内存管理方式,在需要的地方显式地使用了newdelete 但是,很容易忘记使用delete ,并且在出现异常时很难正确使用它。 通常首选的替代方法是使用智能指针 但这是一个有点高级的话题,所以刚开始时,我建议先熟悉显式newdelete ,但请记住,当你准备好时,还有更好的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM