为什么对象在作为指针传递时超出范围,但在返回时却没有

Why does an object go out of scope when passed as a pointer, but not when it is returned

提问人:mreff555 提问时间:3/29/2019 最后编辑:songyuanyaomreff555 更新时间:3/29/2019 访问量:1267

问:

制作一个非常简单的链表时,我发现自己对可能是一个非常简单的范围界定概念感到困惑。第一个按预期工作。似乎在第二个 makeNode 函数中,节点“n”在终止时超出了范围。我很困惑为什么会这样。

我很确定分配的内存仍然存在,并且这两种方法都有一个指向该内存的指针。那么为什么一个不起作用呢?

该程序有两个 make 节点函数

#include <iostream>
using namespace std;

struct Node
{
    int value;
    Node* next;
    Node() : next(NULL){}
};

Node* makeNode1(int value)
{
    Node* n = new Node;
    n->value = value;
    return n;
}

void makeNode2(int value, Node* mountPt)
{
    Node* n = new Node;
    n->value = value;
    mountPt = n;
}

void destroyNode(Node* mountPt)
{
    if(mountPt->next != NULL)
    {
        destroyNode(mountPt->next);
        delete mountPt;
    }
}

int main() {
    Node* origin = NULL;

    // This works
    origin = makeNode1(4);

    // causes a runtime error when dereferenced
    makeNode2(4, origin);

    return 0;
}
C++ 作用域 按引用传递

评论

0赞 Sam Varshavchik 3/29/2019
打开 C++ 书中解释按值传递函数参数与按引用传递函数参数之间的区别的章节,然后阅读。你按值传递它,并泄漏内存。传递给 is 的指针未初始化,并且在调用方中保持未初始化状态,因为它是按值传递的。这就是C++的工作方式。makeNode2
1赞 Vuwox 3/29/2019
中有一个泄漏,你应该添加该行以确保先删除以前的分配,然后再分配。makeNode2if(mountPt) delete mountPt;n
0赞 mreff555 3/29/2019
Vuwox 是正确的。
0赞 Vuwox 3/29/2019
@Amadeus 是的,我知道上面编译,但如果没有删除,这个函数会产生内存泄漏。通过添加它,编译器不会给出使用完整警告级别的意愿,并且运行时错误也不会附加。parameter 'mountPt' set but not used

答:

3赞 songyuanyao 3/29/2019 #1

对于指针参数本身是按值传递的,那么函数内对指针本身的任何修改都与原始参数无关。makeNode2mountPtmountPt = n;origin

您可以将其更改为按引用传递,即

void makeNode2(int value, Node*& mountPt)

评论

0赞 ShadowRanger 3/29/2019
请注意,这样做会导致内存泄漏;分配的原始指针将替换为分配的新指针,而不使用 -ed。originmakeNode1makeNode2delete
0赞 mreff555 3/29/2019
我知道。显然只需要一个函数。我只是简单地展示了这两种配置。
1赞 CodeoftheWarrior 3/29/2019 #2

首先,您创建一个新的内存部分,其中包含指向该内存部分(只是一个地址)的指针,然后返回该地址。第二个示例将指针传递到内存中的地址,然后在函数的作用域中重新分配该地址(即更改该值)。但是,一旦函数退出,该指针的值(它引用的地址)就会恢复到其之前的调用,因为指针在堆栈上已经有一个值。区别在于,在第一次调用中,您更改了存储在最终从函数中传出的地址中的值,而在第二次调用中,您更改了指针指向的地址,但是一旦将作用域更改回主体,该指针就会返回指向旧地址。

评论

0赞 CodeoftheWarrior 3/29/2019
按值传递指针仍允许您永久更改存储在某个地址的值,但不允许更改指针指向的内容。
0赞 user4581301 3/29/2019
“恢复”是一种糟糕的描述方式。源永远不会改变。
0赞 Vuwox 3/29/2019 #3

更改以下功能:

void makeNode2(int value, Node* mountPt)
{
    Node* n = new Node;
    n->value = value;
    if(mountPt) delete mountPt;
    mountPt = n;
}

如果 mountPt 已经分配,它将被释放,然后可以指向分配originn

评论

0赞 mreff555 3/29/2019
我同意这将是更好的内存管理,但这不会有同样的范围问题吗?
0赞 user4581301 3/29/2019
@mreff555 是的,它会的。
0赞 mreff555 3/29/2019
实际上没有。但是,如果将引用运算符附加到 Node* 参数中,如上所述,则它可以工作
0赞 user4581301 3/29/2019 #4

X-Y 解决方案:通过扩展 Node 构造函数来消除对函数的需求makeNode

struct Node
{
    int value;
    Node* next;
    Node(int val = 0) :value(val), next(NULL){}
};

用法

Node * origin = new Node(4);
0赞 Red.Wave 3/29/2019 #5

虽然以上所有内容(包括公认的答案)都是相关的,但没有一个直接指向实际的逻辑缺陷。而不是 ,您需要 。mountPt = n;mountPt->next = n;