通过引用传递表达式与通过引用传递变量

Posted

技术标签:

【中文标题】通过引用传递表达式与通过引用传递变量【英文标题】:Passing an expression by reference vs Passing variable by reference 【发布时间】:2019-07-26 13:58:56 【问题描述】:

在下面显示的代码中,func(a3) 进入 if 条件并产生输出“hi”。但是,当函数的参数是表达式时,会观察到不同的行为。

例如,func(a1->right) 不输入 if 条件。

#include <iostream>
using namespace std;
class Node 
   public:
    int data;
    Node *left, *right, *parent;

   public:
    Node(int data) : data(data) 
        left = nullptr;
        right = nullptr;
        parent = nullptr;
    
    ~Node() 
;
void func(Node*& node) 
    Node* p = node->parent;
    p->right = node->left;
    if (node->left) 
        cout << "hi\n";
        node->left->parent = p;
    
    node->parent = p->parent;

int main() 
    Node* a1 = new Node(10);
    Node* a2 = new Node(20);
    Node* a3 = new Node(30);
    Node* a4 = new Node(40);
    Node* a5 = new Node(50);
    a1->left = a2; a2->parent = a1;
    a1->right = a3; a3->parent = a1;
    a3->left = a4; a4->parent = a3;
    a3->right = a5; a5->parent = a3;
    /*
         a1
       /   \
     a2     a3
           /  \
           a4   a5
     */

    /* Case 1: prints hi */
    func(a3);

    /* Case 2: doesn't print hi */
    // func(a1->right);


    /* Case 3: prints hi */
    // Node* ptr = a1->right;
    // func(ptr);

我有两个问题:

    表达式引用变量引用相比,传递给 func 时出现不同行为的原因是什么?

    表达式的引用传递给函数的惯用方式是什么。

编辑:gdb 输出

(gdb) b 17
Breakpoint 1 at 0x555555554856: file pointer_ref.cpp, line 17.
(gdb) r
Starting program: /home/a.out 

Breakpoint 1, func (node=@0x555555767e80: 0x555555767ed0) at pointer_ref.cpp:18
18      Node* p = node->parent;
(gdb) p node->data 
$1 = 30 // a3
(gdb) n
19      p->right = node->left;
(gdb) p p->data
$2 = 10 // a1
(gdb) n
20      if (node->left) 
(gdb) p p->right->data
$3 = 40 // a4
**(gdb) p node->left->data
Cannot access memory at address 0x0**
// ^^^ This seems to be the problem location
// After changing p->right to node->left,
// somehow, node->left becomes null 
(gdb) p node->left
$4 = (Node *) 0x0
(gdb) 

【问题讨论】:

顺便说一句,不要包含bits/stdc++.h,因为它会带入所有内容,而您只想带入一些内容。 注意点@AndyG。 我现在看到了这个问题。您应该使用调试器逐步完成此操作并观察您的树的外观。我会说没有理由通过func 中的引用接收您的ptr。 你修改了你传入的东西。所以也许它与你进行不同函数调用的顺序有关。 您通过引用传递指针的原因是什么?除了让你的代码过于复杂之外。 【参考方案1】:

您已传递对a1-&gt;right 的引用。因此,您对该字段所做的任何更改都会在该函数中看到。 p-&gt;right = node-&gt;left; 实际上将 a1-&gt;right 设置为不同的节点。

在情况 3 中,您传递对局部变量 ptr 的引用,该变量不会被更改,因为它是一个副本。

如果你添加:

    cout << "node was " << node << std::endl;
    p->right = node->left;
    cout << "node is " << node << std::endl;

你会看到你的节点发生了变化。

【讨论】:

那为什么node->left会变成null(请参考新加入的gdb输出)。 node-&gt;left 变为 null,因为 node 现在指向不同的节点,该节点没有左子节点。 a1->right是a3,所以node->left此时就是a3->left,也就是a5。 a5->left 为 NULL。 (希望我没看错)【参考方案2】:

将表达式的引用传递给 func 而不是变量的引用时出现不同行为的原因是什么?

原因是程序中的复杂逻辑。在这种特殊情况下,当您传递a3a1-&gt;right 时,任一指针都指向同一个对象,但func() 本身会修改a1-&gt;right,因此当对a3 的引用传递时,更改不会影响node,但是当a1-&gt;right 通过了。这就是区别。

将表达式的引用传递给函数的惯用方式是什么。

按照您的方式传递引用没有问题,问题是数据关系过于复杂。例如,在您的情况下,没有理由通过引用传递此指针。

【讨论】:

以上是关于通过引用传递表达式与通过引用传递变量的主要内容,如果未能解决你的问题,请参考以下文章

仅应通过引用传递变量 - 将 LIMIT 与 bindParam 一起使用时

通过引用javascript传递原始变量[重复]

Python:变量何时通过引用传递,何时通过值传递? [复制]

为啥我会收到“仅应通过引用传递变量”错误?

为啥python不允许通过引用传递变量

通过引用和标量变量传递 Perl 哈希