我的C++变量是存在栈上还是堆上?

4

这是我在stackoverflow上的第一个问题。如果这是一个“愚蠢”的问题,那么对不起,但是我正在学习C++,我有点困惑。根据我所理解的,在下面的代码中,变量“myVariable”在堆上声明但在构造函数中在栈上实例化。那么它在哪里“生活”-在堆上还是栈上?

class MyClass{
    int _myVariable;
    MyClass(int i){
        _myVariable = i;
    }
}

2
在这段代码中,它没有声明任何存储位置。它只是一个类的蓝图。它取决于你如何实例化它。 (可能还取决于环境,我不认为标准定义了堆栈之类的东西,这只是通常的做法)。如果你在堆中创建这个类的实例,那么它的所有内容都在堆中。如果你在栈中实例化它,在这种情况下,因为没有指定分配,所有内容都在栈中。 - Sami Kuhmonen
3
它存在于 MyClass 变量所在的位置。如果你使用 new MyClass,它会在堆中。如果你声明一个局部变量,它会在栈中。如果你声明一个全局变量,它会在数据段中。 - Barmar
1
一般来说,没有堆栈和堆;这些都是实现细节。有些系统没有堆栈,有些则没有堆。我的建议是不要试图用这些术语对变量进行分类。相反,应该考虑变量的生命周期和存储类别;对于非静态类成员而言,它们与该类对象的生命周期和存储类别相同。 - M.M
@M.M:没有堆栈?哇! - user1196549
@M.M 不过,还有"自动存储"和"动态存储" :P - Raildex
显示剩余2条评论
4个回答

6
成员变量根据类的实例化方式进行分配。
void example() {
  MyClass a{17}; // This allocates memory on stack
  MyClass *b = new MyClass(17); // This allocates memory on heap
}

我添加了一个函数,因为a看起来像是一个全局变量,通常既不在堆栈上也不在堆上。 - MSalters

5
"栈"和"堆"是实现细节。C++标准定义了自动存储动态存储。 最常用的变量存储实现分别是堆栈和堆。 new操作符将对象存储在动态存储中: MyClass* mc = new MyClass(14);。 请注意,我们现在需要使用指针来访问它。
对于自动存储,我们可以省略指针语义: MyClass mc = MyClass(14); // 在自动存储上构造一个对象
对象生命周期由编译器自动管理,更确切地说是到作用域末尾为止。
对于动态分配的对象,您必须自行管理对象的生存期。
有些情况下不能使用自动存储:即多态性。
如果MyClass是多态的(也就是说,它具有虚函数),则无法将其放入自动存储中,因为编译器需要知道对象的大小。由于在多态性中,指向的对象的大小可能会在运行时发生变化,因此无法在自动存储中创建它。
MyClass c = MyClass2(); //Object Slicing 
c.foo(); // calls MyClass::foo()

MyClass* cc = new MyClass2(); //No Object Slicing
cc->foo(); // calls MyClass2::foo();

有一些辅助类可以帮助你摆脱清理内存的责任:unique_ptrshared_ptr(尽管后者很少使用)。


只是一个类声明,不会分配任何东西。 - Const
“如果MyClass是多态的”这部分是误导性的。你绝对可以将MyClass对象放在自动存储中,而且你甚至展示了你可以这样做。而“对象的大小可以在运行时改变”是错误的;一个对象从其生命周期开始到结束都具有相同的大小。我理解你的意思,但我不是你的目标受众。 - MSalters
@MSalters 请再次阅读:我写的是“指向对象的大小”,而不是“对象的大小可以改变”。另外,MyClass c并非多态的,即使我尝试实例化MyClass2,因为它调用的是MyClass :: foo()而不是MyClass2 :: foo() - Raildex
@Raildex:ISO C++:“11.7.3虚函数:具有虚成员函数的类称为多态类。”如果MyClass :: foovirtual,那么MyClass是多态的。你可以切割MyClass2,但这并不重要;一个类是否是多态的完全取决于标准规定。 - MSalters
@MSalters 我完全没注意到那个。我会相应地编辑我的答案。 - Raildex

3
在给定的代码中,没有分配任何内容,只有一个纯粹的类声明。
"myVariable" 在构造函数中实例化在堆栈上是自相矛盾的(并且错误的)。像您这样的简单构造函数不会进行分配,它只是在创建类实例时赋值。构造函数不会分配实例,析构函数也不会释放它。
分配是在堆上还是在栈上完成取决于您如何使用该类(作为普通变量或指针) 。

3

这得看对象存在的位置。如果实际对象在堆上,变量也在堆上。如果对象存在于栈上,则变量也存在于栈上。

看这个例子:

MyClass myObject = MyClass(5); // myObject lives on the stack and thus the variable i for myObject also lives on the stack
    
MyClass* newObject = new MyClass(5); // newObject is allocated on the heap and the variable i for newObject lives on the heap
    
delete newObject; // since newObject is created using new we must free the memory after use

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接