C++ 什么时候是C++;类在堆栈上实例化?

C++ 什么时候是C++;类在堆栈上实例化?,c++,memory-management,C++,Memory Management,我想澄清一下在堆栈上实例化类时会发生什么 当在堆上实例化C++类: MyClass *myclass = new MyClass(); 创建MyClass类型的指针,并在同一行上通过“new MyClass();”实例化该类。像这样展开: MyClass *myclass; myclass = new MyClass(); 如果我没有弄错的话,在第1行创建一个指针,然后在第2行为实例分配内存,并将指向实例地址的指针分配给myclass 这是否意味着当类以这种方式在堆栈上实例化时: MyCla

我想澄清一下在堆栈上实例化类时会发生什么

当在堆上实例化C++类:

MyClass *myclass = new MyClass();
创建MyClass类型的指针,并在同一行上通过“new MyClass();”实例化该类。像这样展开:

MyClass *myclass;
myclass = new MyClass();
如果我没有弄错的话,在第1行创建一个指针,然后在第2行为实例分配内存,并将指向实例地址的指针分配给myclass

这是否意味着当类以这种方式在堆栈上实例化时:

MyClass myclass = MyClass();

它被创建了两次?

在使用指针的情况下,第一行只是声明了一个指针,正如您所说的。带有new的行不仅仅是分配内存,它还将调用要调用的MyClass的默认构造函数。您可以通过以下方式在一行中完成此操作:

MyClass*MyClass=newmyclass

当MyClass没有参数构造时,MyClass后面的括号是可选的

对象不会在堆栈上创建,并且在指针上调用delete之前将一直存在。如果这种情况不发生,你将有一个漏洞

MyClass MyClass=MyClass()

该类将被创建两次,首先使用默认构造函数,然后使用复制构造函数。编译器可能会将其优化为单个构造,但您实际上应该初始化:

MyClass-MyClass

请注意,声明中不能使用括号,否则它将声明函数而不是类的实例

在这种情况下,将在堆栈上创建一个实例。您的方法可能会在复制构造的过程中创建一个“临时”堆栈变量。您可以将临时性的概念看作是构造函数的“返回”,返回值是一个棘手的区域,一般是自动的,使用堆栈空间。

“堆栈”和“堆”在C++中没有定义,内存不需要分配到任何地方。 以您为例:

MyClass myclass = MyClass();
您正在通过将构造函数复制到临时对象来初始化myclass<代码> MyCase< /Calp>(和临时)具有自动存储,您可以考虑将其分配到“堆栈”上,如果这使您满意。

在这种情况下,复制省略是允许的,本质上是将其优化为
MyClass MyClass
,但是请注意,它不能总是这样做,例如复制构造函数是私有的

以下是您可以测试的示例:

struct obj {
  static int c;
  int myc;
  obj() : myc(c++) {
    std::cout << "ctor of " << myc << '\n';
  }
  obj(const obj&) : myc(c++){
    std::cout << "copy ctor of " << myc << '\n';
  }
  ~obj() {
    std::cout << "dtor of " << myc << '\n';
  }
};
int obj::c = 1;

int main(int argc, char** argv)
{
  obj x = obj();
}
否则(gcc选项-fno省略构造函数以防止省略发生):


此外,将复制构造函数设置为私有将导致编译器错误。

我的文章只是为了完成Luchian Grigore的文章,并解决nabulke的问题

事实上,
MyClass MyClass=MyClass()不调用赋值运算符在C++96规范第12.6.1.1()段中有说明。该行表示:“可以使用=初始化形式将单个赋值表达式指定为初始值设定项。”


我希望这有助于

就标准而言,没有堆栈和堆的概念。但是,我知道的所有C++实现都将映射“自动存储持续时间”和“动态存储”的概念分别映射到堆栈(*)和堆。 (*)如
@MooingDuck
所述,这仅适用于函数变量。全局变量和静态变量(可能)具有自动存储持续时间,但它们不在堆栈上


现在,这是明确的:

  • 函数体中的变量存储在堆栈上
  • new
    创建的对象存储在堆中(并返回其地址)
举例来说,更直观一点:

void f0() {
  Class* c = new Class();
}

void f1() {
  Class* c = 0;
  c = new Class();
}
此处
c
(类型为
Class*
)存储在堆栈上,并指向存储在堆上的对象(类型为
Class

void f2() {
  Class c = Class();
}

void f3() {
  Class c;
}
这里
c
存储在堆栈上。在
f2
中,可能有一个由表达式
Class()
创建的临时对象(没有名称),然后复制到
c
(取决于编译器是否省略了副本),临时对象的存储不受标准的约束。。。不过,它们通常使用堆栈


最后一句话:这是否真的使用了堆栈上的一些空间是另一回事

  • 编译器可能完全不需要对象
  • 变量可以存储在堆栈或寄存器(CPU特定的“插槽”)中
在行动中:

// Simple test.cpp
#include <cstdio>

struct Class { void foo(int& a) { a += 1; } };

int main() {
  Class c;

  int a = 0;

  c.foo(a);

  printf("%d", a);
}
注意:1。该类已被删除,2。已删除对
foo
的调用,3<代码>a
甚至没有出现。翻译回到C++中得到:

#include <cstdio>

int main() {
  printf("%d", 1);
}

请注意常量(
$1
$.L.str
)是如何被推入寄存器(
%esi
%esi
分别)的,并且从不“命中”堆栈。唯一的堆栈操作是
pushq
popq
(我不知道他们实际上保存/恢复。< /P>即使使用编辑,这个答案也是错误的。没有临时的创建。在你的答案中提到的类之后的括号是可选的,但是如果使用可以导致正确的,我改变了我的投票。但是只有当你认为自动存储是堆栈。标准。只讨论实例的初始化,而不是临时存储的位置。但是,笼统地说,你是对的。+1当人们提到堆栈和堆时,他们通常指的是自动和自由存储。当然,在自由存储中创建的对象的“自动”成员变量(即使用new)从技术上讲,MyClass MyClass=MyClass()应该调用默认构造函数和复制构造函数,因为它们都必须是可访问的,但实际上,复制可能永远不会发生。有一件事它定义了
// Simple test.cpp
#include <cstdio>

struct Class { void foo(int& a) { a += 1; } };

int main() {
  Class c;

  int a = 0;

  c.foo(a);

  printf("%d", a);
}
@.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1

define i32 @main() nounwind uwtable {
  %1 = tail call i32 (i8*, ...)* @printf(@.str, i32 1)
  ret i32 0
}
#include <cstdio>

int main() {
  printf("%d", 1);
}
main:                        # @main
pushq   %rax             # save content of 'rax' on the stack
movl    $.L.str, %edi    # move address of "%d" into the 'edi' register
movl    $1, %esi         # move 1 into the 'esi' register
xorb    %al, %al         # --
callq   printf           # call printf, it'll look up its parameters in registers
xorl    %eax, %eax       # --
popq    %rdx             # restore content from stack to 'rdx'
ret                      # return