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