【问题标题】:Why does an object go out of scope when passed as a pointer, but not when it is returned为什么一个对象在作为指针传递时超出范围,但在返回时却没有
【发布时间】:2019-08-19 21:38:00
【问题描述】:

制作一个非常简单的链表我发现自己对什么可能是一个非常简单的范围概念感到困惑。第一个按预期工作。似乎在第二个 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++ 书籍中解释按值传递函数参数与通过引用传递函数参数之间的区别的章节,然后阅读它。您按值传递它,makeNode2 泄漏内存。传递给 is 的指针未初始化,它在调用者中保持未初始化,因为它是按值传递的。这就是 C++ 的工作原理。
  • makeNode2有泄漏,你应该添加if(mountPt) delete mountPt;这一行,确保先删除之前的分配,然后再分配n
  • Vuwox 是正确的。
  • @Amadeus 是的,我明白上面的编译,但是没有删除,这个函数会造成内存泄漏。并且通过添加它,编译器不会给出parameter 'mountPt' set but not used 将使用完整的警告级别,并且运行时错误也不会附加。

标签: c++ scope pass-by-reference pass-by-value


【解决方案1】:

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

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

void makeNode2(int value, Node*& mountPt)

【讨论】:

  • 请注意,这样做会导致内存泄漏;在makeNode1 中分配的原始origin 指针被makeNode2 中分配的新指针替换,而不是delete-ed。
  • 我知道。显然只需要一个函数。我只是展示了这两种配置。
【解决方案2】:

在第一个实例中,您正在创建一个新的内存部分,并带有指向该内存部分的指针(这只是一个地址),然后返回该地址。第二个示例传入一个指向内存中地址的指针,然后在函数范围内重新分配该地址(即更改该值)。但是,一旦函数退出,该指针的值(它所指的地址)将恢复到其先前的调用,因为该指针已经在堆栈上具有值。不同之处在于,第一次调用时,您更改了存储在最终从函数中传递出来的地址中的值,而在第二次调用中,您更改了指针指向的地址,但该指针返回指向旧地址一旦您将范围更改回主体。

【讨论】:

  • 按值传递指针仍然允许您永久更改存储在地址中的值,但不允许您更改指针指向的内容。
  • “恢复”是一种糟糕的描述方式。来源永远不会改变。
【解决方案3】:

更改以下功能:

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

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

【讨论】:

  • 我同意这将是更好的内存管理,但这不会有相同的范围问题吗?
  • @mreff555 是的。
  • 实际上没有。但是如果引用运算符附加到 Node* 参数上,如上所述,它可以工作
【解决方案4】:

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

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

用法

Node * origin = new Node(4);

【讨论】:

    【解决方案5】:

    虽然以上所有内容 - 包括已接受的答案 - 都是相关的,但没有一个直接指向实际的逻辑缺陷。你需要mountPt-&gt;next = n;,而不是mountPt = n;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-16
      • 2021-10-17
      • 2012-03-02
      • 2021-08-16
      • 2016-05-05
      • 1970-01-01
      • 2012-04-22
      相关资源
      最近更新 更多