堆栈上的 C++ 实例变量存储为指针

Posted

技术标签:

【中文标题】堆栈上的 C++ 实例变量存储为指针【英文标题】:C++ instance variable on stack stored as a pointer 【发布时间】:2015-06-05 14:02:27 【问题描述】:

我在堆栈上创建了一组简单的 Dummy 对象:

    Dummy a(8);
    Dummy b(9);
    Dummy dummies[2] = a,b;

有一个 MyObject 对象。我在堆栈上创建了它。构造函数将这个数组存储到一个实例变量中。

    class MyObject
        Dummy* _dummies;

        MyObject obj(Dummy* dummies):
        : _dummies(dummies)
        
        
    ;

    MyObject obj(dummies); 

当obj被移除并调用其析构函数时,_dummies的内存是否被释放?我知道堆栈上的实例变量是自动释放的,但是 MyObject 怎么知道这个指针指向堆栈上的数组,而不是指向在堆上创建的 Dummy 对象?

在构造函数中,虚拟数组是按值传递还是按引用传递?它是一个指针,但它毕竟是堆栈上的一个数组。

我了解堆栈、堆、按值和引用传递,但堆栈上的数组用指针处理真的让我很困惑。我一直用vector,因为它清晰简单,但我也想了解简单的数组。

编辑:谢谢大家。现在我明白 MyObject 与数组的生命周期没有任何关系。当虚拟数组超出范围时,它会被删除。

但是:如果将“Dummy”替换为“char”会怎样?在堆栈上有一个 char* (例如从函数返回)并将其传递给一个对象,然后通过将指针保存到第一个 char 来存储它是非常标准的。那也是一种不好的做法吗? (我将使用 std::vector 和 std::string 代替,更简单...)

【问题讨论】:

你有“新”的东西吗?如果没有,不用担心。 除非你通过“new”创建内存,否则无需担心释放它。当它超出范围时,它将从堆栈中弹出。 这样的数组根本没有传递给构造函数。构造函数接收指向数组第一个元素的指针。 【参考方案1】:

您需要注意,您的实例变量 _dummies 只是一个指针。创建对象时,传递给构造函数的指针会存储到 _dummies 中,仅此而已。该指针可以是 NULL、堆栈上的数组地址、malloc 返回的指针或许多其他东西。你的对象不知道。

如果数组消失而对象仍然存在,那么如果在此之后使用该对象就会有麻烦。您有责任确保数组仍然存在,直到最后一次访问对象中的 _dummies。并且您负责在对象消失后以某种方式删除数组本身。所以你的构造有点危险,除非你小心使用它会崩溃。

【讨论】:

【参考方案2】:

您唯一需要担心的是,您是否在 Dummy 类中使用了 new。如果您没有,则无需担心。当堆栈上的对象超出范围时,它们会被自动删除。

【讨论】:

【参考方案3】:

当obj被移除并调用其析构函数时,_dummies的内存是否被释放?

不,但这不是问题,如果我正确理解了您的示例,因为“_dummies 的内存”在堆栈上,当它超出范围时将被释放(独立于被销毁的 MyObject 实例)。

我知道堆栈上的实例变量是自动释放的,但是 MyObject 怎么知道这个指针指向堆栈上的一个数组,而不是指向在堆上创建的 Dummy 对象?

它没有。正如我上面所说,MyObject 析构函数不会自动释放 _dummies 指向的任何已分配内存。

在构造函数中,虚拟数组是按值传递还是按引用传递?它是一个指针,但它毕竟是堆栈上的一个数组。

它是堆栈上的数组,而不是指针。在表达式中使用时,它“衰减”为指针(指向数组中的第一个元素)。因此,它是通过引用传递的(但不是作为 C++ 引用!)。

【讨论】:

【参考方案4】:

您的Dummy* _dummies; 成员变量可能指向也可能不指向某个实际内存位置。管理此位置是您的责任,而不是默认构造函数。因此,如果您使用类外部的指针对其进行初始化,则当实例被销毁时,指向的内存不会发生任何事情,只是指针会消失。如果你在你的类中创建(新)一个实例,你必须在析构函数中释放(删除)它。

从实际的角度来看,您可能需要考虑智能指针,例如 C++11 中的 std::shared_ptr 以使这些任务更不容易出错。

【讨论】:

【参考方案5】:

当obj被移除并调用其析构函数时,_dummies的内存是否被释放?

不,不是。如果你想释放与任何指针相关的内存,你必须明确地这样做,例如如果_dummies 是动态数组,则输入delete[] _dummies

MyObject 怎么知道这个指针指向栈上的一个数组而不是堆上创建的 Dummy 对象?

它没有。这意味着如果您在堆栈分配的Dummy dummies[2] = a,b; 超出范围并被删除后尝试访问MyObject 中的_dummies,您可能会遇到段错误。

在构造函数中,虚拟数组是按值传递还是按引用传递?

其类型Dummy[]被强制转换为指针类型Dummy*,然后按值传递。

【讨论】:

你不应该 delete[] _dummies如果它指向的数组是在堆栈上分配的,就像在OPs问题中一样。

以上是关于堆栈上的 C++ 实例变量存储为指针的主要内容,如果未能解决你的问题,请参考以下文章

C# 中结构的实例和成员的堆和堆栈如何工作?

C++ 父类指针如何调用子类的变量和函数 虚函数virtual实例

C++ 父类指针如何调用子类的变量和函数 虚函数virtual实例

对象的实例变量存储在 JVM 中的啥位置?

存储在堆栈或堆中的对象方法?

以编程方式在 MSVC 和 Clang 上设置堆栈指针