【问题标题】:Why are these pointers the same, while referring to different instances of an object?为什么这些指针是相同的,却指向一个对象的不同实例?
【发布时间】:2021-04-17 00:42:12
【问题描述】:

我正在调试我为二叉搜索树创建的复制构造函数,我通过监视原始树中节点的内存地址以及新创建的副本中的内存地址来做到这一点。节点由指向 Comparable 项(仅包含一个字符)的指针、该字符的出现次数以及指向其子节点(或 nullptr)的指针组成。

  struct Node {
    int count; // the number of occurences of the Comparable item (a char in this case)
    Comparable* item; // the Comparable item being tracked
    Node* leftChild;
    Node* rightChild;
  };

这是我的复制构造函数和助手:

BST::BST(const BST& tree)
{
  rootPtr = copy(tree.rootPtr);
}

BST::Node* BST::copy(Node* root)
{
  if (root == nullptr) {
    return nullptr;
  }

  Node* temp = new Node;
  Comparable* ptr = new Comparable;
  *ptr = *root->item;

  temp->item = ptr;
  temp->count = root->count;
  temp->rightChild = copy(root->rightChild);
  temp->leftChild = copy(root->leftChild);

  return temp;
}

我正在使用此函数将两棵树的数据以及它们在内存中的地址输出到控制台:

void BST::printInOrder(Node* root) const
{ 
  if (root->leftChild != nullptr) {
    printInOrder(root->leftChild);
  }

  cout << "&root: " << &root << " root: " << root << " :: " << *root->item << " " << root->count << endl;
  // example output: &root: 00EFF388 root: 0113E858 :: W 1

  if (root->rightChild != nullptr) {
    printInOrder(root->rightChild); 
  }
}

这是原始树(顶部)和复制构造函数构造的树(底部)的控制台输出:

  // original tree
    &root: 00EFF470     root: 01140760 :: ! 1
    &root: 00EFF558     root: 0113A018 :: H 1
    &root: 00EFF388     root: 0113E858 :: W 1
    &root: 00EFF2A0     root: 011406F0 :: d 1
    &root: 00EFF470     root: 01134940 :: e 1
    &root: 00EFF388     root: 01134A80 :: l 3
    &root: 00EFF2A0     root: 0113E7E8 :: o 2
    &root: 00EFF1B8     root: 01140680 :: r 1
    
  // copy of original tree
    &root: 00EFF470     root: 01140F90 :: ! 1
    &root: 00EFF558     root: 011407A0 :: H 1
    &root: 00EFF388     root: 01141390 :: W 1
    &root: 00EFF2A0     root: 01140C90 :: d 1
    &root: 00EFF470     root: 01140BE8 :: e 1
    &root: 00EFF388     root: 011410D0 :: l 3
    &root: 00EFF2A0     root: 01140E10 :: o 2
    &root: 00EFF1B8     root: 01140DD0 :: r 1

您会注意到,对于两棵树,第二列 (root:) 包含从第一棵树到第二棵树的一组完全不同的内存地址,这是有道理的,并且让我相信我的复制构造函数作为Node* 指针指向内存中的不同地址(而这些地址包含相同的值)。

第一列 (&amp;root:) 是我的困惑所在。我不明白为什么两棵树中的节点会用同一个指针指向它们的指针(这更适合图表)。

//column 1:              //column 2:
//identical addresses    //different addresses
&root: 00EFF470          root: 01140760 :: ! 1
&root: 00EFF470          root: 01140F90 :: ! 1
         

任何有关在何处进一步研究的见解或方向都会有所帮助,我当然是 C++ 新手,哦,如果这对此有所不同,我正在使用 Visual Studio 2019。

【问题讨论】:

  • 显示创建/复制/打印树的代码。仅仅功能还不够运行程序。
  • 如果将void BST::printInOrder(Node* root) const 更改为void BST::printInOrder(Node*&amp; root) const 会发生什么? (我无法测试它,因为您的代码不是minimal reproducible example。)
  • 当您打印&amp;root 时,您会打印局部变量root 的位置,它与树或其中的节点完全分开。

标签: c++ pointers memory-management copy-constructor


【解决方案1】:

&amp;root 是变量根的地址。变量root是一个指针,但是那个指针存在于栈中。

所以指针就像一张纸,上面有街道地址。 root是一张纸,上面写着*root的地址。

&amp;root那张纸所在的位置。堆栈就像一条特殊的道路,您始终在其中构建和销毁“本地”变量存储。 root,这张纸,就在当地的堆栈街上。 &amp;root 是本地堆栈“街道”上那张纸的地址。

每次调用函数时,堆栈上都会为其局部变量和参数保留一些空间。函数调用完成后,该空间将被重用。

由于堆栈被重用,两个不同的函数将使用相同的内存——相同的地址——来存储不同的数据。当您调用一个函数,让它返回,然后调用另一个函数时,就会发生这种情况。

【讨论】:

  • 谢谢!这真的帮助我解决了这个问题。因此,如果我在为第一棵树调用printInOrder 之后没有立即为第二棵树调用它,那么当我最终在第二棵树上调用printInOrder 时,我可能会使用不同的内存地址?其实不用回答,我自己去玩看看。再次感谢!
【解决方案2】:

以下是代码中的关键行:

void BST::printInOrder(Node* root) const
{ 

  cout << "&root: " ...

只检查这几行,看看这两行,然后问自己以下问题:

什么是&amp;root

将此视为一个流行测验。在阅读下面的答案之前,给自己五秒钟的时间来想出答案:

...

&amp;root 当然是printInOrder 的“root”参数的地址。因此,当您两次调用此函数以打印两个不同的二叉树时,恰好在两个函数调用中,参数恰好位于相同的内存地址,在自动范围内。这就是为什么您每次都看到相同的内存地址。

也许您打算将 reference 作为此参数传递给根节点?

换句话说:

void BST::printInOrder(Node *& root) const

【讨论】:

    猜你喜欢
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 2020-01-18
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    相关资源
    最近更新 更多