为什么这些指针是相同的,而引用对象的不同实例?

Why are these pointers the same, while referring to different instances of an object?

提问人:letthewookieewin 提问时间:1/12/2021 更新时间:1/13/2021 访问量:151

问:

我正在调试我为二叉搜索树创建的复制构造函数,我通过监视原始树以及新创建的副本中节点的内存地址来做到这一点。节点由指向 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

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

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

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

任何关于在哪里更多地研究这个问题的见解或方向都会有所帮助,我当然是一个C++新手,哦,我正在使用 Visual Studio 2019,如果这对此有所作为。

C++ 指针 内存管理 复制构造函数

评论

1赞 user202729 1/12/2021
同时显示创建/复制/打印树的代码。仅功能不足以运行程序。
3赞 JaMiT 1/12/2021
如果更改为 ?(我无法测试它,因为您的代码不是一个最小的可重现示例void BST::printInOrder(Node* root) constvoid BST::printInOrder(Node*& root) const
3赞 Some programmer dude 1/12/2021
打印时,打印局部变量的位置,该变量与树或其中的节点完全分开。&rootroot

答:

3赞 Sam Varshavchik 1/12/2021 #1

下面是代码中的关键行:

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

  cout << "&root: " ...

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

什么&root

把这想象成一个流行测验。在阅读下面的答案之前,给自己五秒钟的时间想出一个答案:

...

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

也许你的意思是将引用传递给根节点,作为这个参数?

换言之:

void BST::printInOrder(Node *& root) const
2赞 Yakk - Adam Nevraumont 1/12/2021 #2

&root是变量 root 的地址。变量 root 是一个指针,但该指针存在于堆栈上。

因此,指针就像一张纸,上面写着街道地址。 是一张纸,上面写着地址。root*root

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

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

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

评论

0赞 letthewookieewin 1/13/2021
谢谢!这真的帮助我解决了这个问题。因此,如果我没有在为第一棵树调用第二棵树后立即调用它,那么当我最终调用第二棵树时,我可能会使用不同的内存地址?其实,不要回答这个问题,我自己去玩看看。再次感谢!printInOrderprintInOrder