简体   繁体   English

C++| BST 引用节点指针与节点指针

[英]C++| BST reference-to-node-pointer vs. a node-pointer

Suppose I have this BST template:假设我有这个 BST 模板:

template <typename T> class Node {
private:
public:
  T data;
  Node *left;
  Node *right;

  Node(T dt) : data{dt}, left{nullptr}, right{nullptr} {}
  ~Node() {
    this->data = 0;
    this->left = nullptr;
    this->right = nullptr;
  }
};

template <typename T> class BST {
private:
  Node<T> *_root;

  _insert();
  _add();
  _printOrder_In(Node<T> *parent, std::ostream& os) {
    if (!parent) return;
    _printOrder_In(parent->left, os);
    os << parent->data << ' ';
    _printOrder_In(parent->right, os);
  }

public:
  BST() : _root{nullptr} {}
  ~BST();
  
  insert();
  add();
  std::ostream& print(std::ostream& os = std::cout) {
    _printOder_In(this->_root, os);
    return os;
  }
};

Why do the following code work when I pass a reference-to-the-node-pointer, not work when I pass the-node-pointer?为什么以下代码在我传递对节点指针的引用时有效,而在传递节点指针时不起作用?

// BST MEMBER FUNCTIONS:
private:
  void _insert(Node<T>* &parent, const T &val) { // works
//void _insert(Node<T>*  parent, const T &val) { // doesn't work, apparently generates nodes indefinitely
    if (!parent)
      parent = new Node<T>{val};
    else {
      if (val < parent->data)
        _insert(parent->left, val);
      else if (val > parent->data)
        _insert(parent->right, val);
      else
        return;
    }
  }

public:
  void insert(const T &val) {
    _insert(this->_root, val);
  }
};

Also as opposed to this alternate method, which simply works with the passed pointer:也与此替代方法相反,该方法仅适用于传递的指针:

// BST MEMBER FUNCTIONS:
private:
  void _add(Node<T>* parent, T val) {
    if (parent->data > val) {
      if (!parent->left) {
        parent->left = new Node<T>{val};
      } else {
        _add(parent->left, val);
      }
    } else {
      if (!parent->right) {
        parent->right = new Node<T>{val};
      } else {
        _add(parent->right, val);
      }
    }
  }

public:
  void add(T val) {
    if (this->_root) {
      this->_add(this->_root, val);
    } else {
      this->_root = new Node<T>(val);
    }
  }

I understand that a reference-to-the-point will give me direct access to the passed pointer.我知道对点的引用将让我直接访问传递的指针。 However, I am stuck at the difference between the two methods.但是,我坚持两种方法之间的区别。 In the second method, the local copy used in the controlflow still works despite the pointer itself not being passed as reference.在第二种方法中,尽管指针本身没有作为引用传递,但控制流中使用的本地副本仍然有效。

OPs issue is about call-by-value vs. call-by-reference . OP 问题是关于call-by-value 与 call-by-reference

The language C (the “anchestor” of C++) provides exclusively call-by-value. C 语言(C++ 的“先驱”)专门提供按值调用。 The missing call-by-reference can be mimed by using the address of a variable instead of the variable itself.可以通过使用变量的地址而不是变量本身来模拟丢失的按引用调用。 (Of course, the parameter of the resp. function has to become a pointer-to-type instead of the type itself.) (当然,resp. 函数的参数必须成为指向类型的指针,而不是类型本身。)

So, a pointer is passed-by-value but its value can be used to access something outside the scope of the function, and the modification (done at its original storage) will survive the return from that function.因此,指针是按值传递的,但它的值可用于访问函数范围之外的内容,并且修改(在其原始存储中完成)将在该函数返回后继续存在。

When C++ evolved out of C, this principle has been taken over.当 C++ 从 C 演变而来时,这个原则就被接管了。 However, C++ added the call-by-reference like it is known from other comparable languages (eg Pascal).但是,C++ 添加了引用调用,就像从其他类似语言(例如 Pascal)中已知的那样。

A simple demonstration of call-by-value vs. call-by-reference:按值调用与按引用调用的简单演示:

#include <iostream>

void callByValue(int a)
{
  std::cout
    << "callByValue():\n"
    << "  a: " << a << '\n'
    << "  a = 123;\n";
  a = 123;
  std::cout
    << "  a: " << a << '\n';
}

void callByRef(int &a)
{
  std::cout
    << "callByRef():\n"
    << "  a: " << a << '\n'
    << "  a = 123;\n";
  a = 123;
  std::cout
    << "  a: " << a << '\n';
}

int main()
{
  int b = 0;
  std::cout << "b: " << b << '\n';
  callByValue(b);
  std::cout << "b: " << b << '\n';
  callByRef(b);
  std::cout << "b: " << b << '\n';
}

Output:输出:

b: 0
callByValue():
  a: 0
  a = 123;
  a: 123
b: 0
callByRef():
  a: 0
  a = 123;
  a: 123
b: 123

Explanation:解释:

  • The change of a has a local effect only in callByValue() because a is passed by value.的变化a只在局部作用callByValue()因为a是值传递。 (Ie a copy of the argument is passed to the function.) (即参数的副本被传递给函数。)
  • The change of a modifies the passed argument in callByRef() because a is passed by reference. a更改会修改callByRef()传递的参数,因为a是通过引用传递的。

Easy-peasy?十分简单? Of course.当然。 But that's exactly the same if parameter type int of a is replaced by any other type – eg Node* or even Node<T>* .但是,如果参数类型这是完全相同的inta是由任何其它类型的置换-例如Node*或甚至Node<T>*

I took out the relevant lines from OPs code:我从 OPs 代码中取出相关行:

  void _insert(Node<T>* &parent, const T &val) { // works
    if (!parent)
      parent = new Node<T>{val};

If the value of argument parent is a nullptr then the parent is assigned the address of a new created Node<T> .如果参数parent值为nullptr ,则为parent分配新创建的Node<T>的地址。 Thereby, the pointer (variable) passed by reference is modified.从而修改通过引用传递的指针(变量)。 Hence, the modification persists after leaving the function _insert() .因此,在离开函数_insert()后修改仍然存在。

The other alternative:另一种选择:

  void _insert(Node<T>*  parent, const T &val) { // doesn't work, apparently generates nodes indefinitely
    if (!parent)
      parent = new Node<T>{val};

If the value of argument parent is a nullptr then the parent is assigned the address of a new created Node<T> .如果参数parent值为nullptr ,则为parent分配新创建的Node<T>的地址。 Thereby, the pointer is passed by value.因此,指针按值传递。 So, the (original) variable (which was used in the call) is not changed – and still contains the nullptr when the function is left.因此,(原始)变量(在调用中使用)没有改变——并且在函数离开时仍然包含nullptr

Btw.顺便提一句。 According to this, the address of the created Node<T> gets lost.据此,创建的Node<T>的地址将丢失。 (It's not stored anymore anywhere.) However, the Node<T> instance still resides in their allocated memory – inaccessible until end of process – degraded to a piece of wasted memory. (它不再存储在任何地方。)然而, Node<T>实例仍然驻留在它们分配的内存中——在进程结束前不可访问——降级为一块浪费的内存。 This is an example how memory-leaks may occur.这是一个如何发生内存泄漏的示例。

Please, don't confuse this fact with the other that the pointer itself “mimes” a pass-by-reference.请不要将这个事实与指针本身“模仿”传递引用的另一个事实混淆。 Modifications of the object (of type Node<T> ) the pointer points to (if it's not nullptr ) will become persistent.指针指向的对象(类型为Node<T> )的修改(如果它不是nullptr )将变得持久。

Having a closer look onto _add() it appears that only the pointed object (of type Node<T> ) is modified but never the pointer itself.仔细查看_add() ,似乎只有指向对象(类型Node<T> )被修改,但从未修改指针本身。 So, passing it by value is completely sufficient and fine.因此,按值传递它是完全足够的。

But for the correct working of _insert() , modifications of parent itself have to become persistent as well.但是为了_insert()的正确工作,对parent本身的修改也必须变得持久。 Thus, only the first alternative works correctly.因此,只有第一个选项才能正常工作。

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

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