【问题标题】:How can I allocate memory on the stack for a pointer to an object?如何在堆栈上为指向对象的指针分配内存?
【发布时间】:2020-07-26 00:43:17
【问题描述】:

我正在用 C++ 实现一个链接的字符串堆栈,主要是为了好玩。列表的每个对象都是Node 的实例化,而列表本身是StackOfStrings 的实例。现在我使用new 实例化一个新的Node 并且一切正常,但我似乎无法弄清楚如何在堆栈上实例化Node 并在稍后的代码中引用它。

我尝试在 push() 方法中将 first = new Node; 更改为 first = &Node();,但是当我调用 pop() 方法时出现运行时 std::bad_alloc 错误。我的猜测是,这个Node 在堆栈上创建,并在程序退出push() 方法后立即销毁。所以我的问题是:有没有一种方法可以创建分配给first 在堆栈上创建的Node 并保留它以供以后使用?

我包含了代码的基本内容,我在导致我出现问题的行旁边添加了注释。

class StackOfStrings
{
public:
    StackOfStrings() {}; // Could be omitted, default constructor

    void push(std::string item)
    {
        Node* old_first = first;
        first = new Node;         // *** my question revolves around this line ***
                                  // ***    "first = &Node()" does not work    ***
                                  // ***  how can I have a Node on the stack?  ***
        first->setItem(item);
        first->setNext(old_first);
    }

    std::string pop()
    {
        std::string item = first->getItem();
        first = first->getNext();
        return item;
    }

private:
    Node *first = nullptr;
};

为了完整起见,这是Node 类:

class Node
{
public:
    std::string getItem() { return item; }
    Node* getNext() { return next; }
    void setItem(std::string item) { this->item = item; }
    void setNext(Node *node) { this->next = node; }

private:
    std::string item;
    Node* next = nullptr;
};

编辑:我错误地写了 *Node 而不是 &Node。我更新了问题。

编辑 2:我尝试的一个小实验:

  • 我调用了StackOfStringspush()方法在链表中插入单词“hello”。
  • NodesetItem() 方法中插入了一个断点,以检查世界“hello”是否保存在内存中。确实,在内存地址0x00FFF900,我找到了值68 65 6c 6c 6f,对应于ASCII中的“hello”
  • 然后我在对getItem() 的调用中放置了另一个断点,以便再次分析相同的内存地址。我现在发现 a0 fb ff 00 28 是垃圾,当然不是“你好”。

【问题讨论】:

  • 不,堆栈是临时的,它上面的任何值在你的函数结束时都变得无效。为什么要使用栈而不是堆?
  • @AlanBirtles 谢谢。真的没有什么特别的原因,我只是想看看是否有可能以某种方式做到这一点。
  • @giacomo-b 你能告诉我们实际的错误吗?为什么要在堆栈上分配 Node 对象(正如@AlanBirtles 提到的堆栈变量在函数末尾被销毁)。语法 first = *Node() 不是有效的 C++ 语法。
  • 下次调用例程时,它会将该堆栈内存用于其他用途。这就是堆栈的工作原理。
  • @giacomo-b “我现在发现的 a0 fb ff 00 28 是垃圾,当然不是“你好”。”。如前所述,这是因为局部变量在函数退出并释放内存时被破坏。如果您有一些问题要尝试以这种方式解决,那么我认为您需要提供有关该问题的更多信息(XY problem),也许我们可以帮助您找到正确的解决方案,否则在堆栈上分配变量并使用它们当堆栈框架被销毁是一个非常糟糕的主意,我认为你无法让它工作。

标签: c++ pointers stack new-operator heap-memory


【解决方案1】:

如何在堆栈上为指向对象的指针分配内存?

通过创建自动变量。像这样:

void foo() {
    Node* ptr; // this pointer is allocated on the execution stack

有没有一种方法可以创建分配给第一个在堆栈上创建的节点

当然。创建一个自动Node,并使用addressof操作符:

Node node;
StackOfStrings sos;
sos.first = &node;

这个简单的例子违反了你的类的访问说明符。例如,您可能希望将指针传递给构造函数,以避免需要更改访问说明符。

并保留它以备后用?

自动变量(即分配在执行堆栈上的所有变量)在其作用域结束时被销毁。没有例外。如果以后使用在那个范围内,那就没有问题了。如果后面的使用超出了这个范围,那么想要使用执行堆栈是错误的。

【讨论】:

  • 感谢您的回答,不过我对您的示例有疑问。 sos.first = &first 是什么意思?我不是指我的具体案例的访问冲突,假设一切都是公开的,如果它更容易的话。但我想知道&first 指的是什么,因为您还没有定义first。谢谢。
  • @giacomo-b 抱歉,我改变了对变量名称的看法,但忘记更新该行。应该是&node
  • 好了,现在一切都说得通了!谢谢
【解决方案2】:

首先这是一个非常非常糟糕的主意。

其次,您不应获取临时地址,因为一旦到达分号,它将被销毁,从而使您的指针指向无效对象。我认为这一行甚至不应该编译!你在使用任何可视化 c++ 扩展吗?

first = &Node();

您通过调用Node() 在堆栈上构造一个临时地址,然后将其地址绑定到first 指针,然后临时地址被销毁。

你得到的第三个例外是因为这个:

您的getItem() 方法通过复制而不是通过引用返回,因此一旦在悬空指针first 上调用pop(),就会调用std::string 复制构造函数来复制成员std::string item,这是无效的,因为它是从一开始就被摧毁。

std::string 的复制构造函数中,它读取另一个字符串的大小,这恰好是堆栈上的一些随机字节,因为原始字符串已被破坏。

这个数字非常大,Windows 无法在堆上为其分配内存,因此会抛出 std::bad_alloc 类型的异常

如果数字足够小但不小于std::string 内部堆栈数组,您可能会遇到访问冲突,因为复制构造函数将尝试从无效内存地址复制缓冲区。甚至更糟!如果这个块与程序中的任何其他对象相关,你可能会破坏堆或读取一些随机字节

【讨论】:

  • 感谢您的回答,这真的说明了发生了什么!实际上不,我没有使用任何特定的扩展名。我通常使用 g++ 在 Linux 上编译,但今天我使用的是我的另一个 Windows 分区。我也应该尝试用 g++ 编译,看看会发生什么。为什么你认为它不应该编译?我的意思是,我知道我写的内容不正确,但我不只是为指针分配地址吗?从编译器的角度来看,这不是可以接受的吗?
  • 是的,编译器不应该允许这个代码:stackoverflow.com/questions/2280688/…
猜你喜欢
  • 1970-01-01
  • 2021-09-01
  • 2017-12-06
  • 2020-03-09
  • 2012-09-16
  • 2019-10-23
  • 2013-07-23
  • 2015-06-07
  • 2022-01-23
相关资源
最近更新 更多