使用C ++时, 如果有一个班级:
class MyClass
{
char memory1bye;
int memory4bytes;
int another4bytes;
};
这个类在内存中总共使用了9个字节...所以如果我做了类似的事情:
MyClass *t1;
这将为我提供Class的可用地址,但它会分配9个字节吗?它会调用默认构造函数吗? 或者我需要将这9个字节malloc给该类吗? 如果那时我打电话给:
t1 = (MyClass *)new MyClass;
是否会被视为内存泄漏?换句话说,旧地址会发生什么?
答案 0 :(得分:14)
MyClass *t1
定义了一个未初始化的指针。取消引用它会调用undefined behavior。t1 = (MyClass *)new MyClass;
在堆上分配内存并创建一个新对象。如果未使用delete
取消分配此内存,则会出现内存泄漏。此外,您不需要在那里投射,t1 = new MyClass();
就足够了。修改关于分配。
MyClass *t1 = NULL;
声明指向MyClass
- 对象的指针,但它不会创建对象。该指针初始化为指向0
。现在当你做
t1 = new MyClass();
运算符new
创建MyClass
的新实例,并将该对象的地址分配给t1
。您现在可以通过t1
:
t1->doStuff();
您甚至可以创建更多指向同一对象的指针:
MyClass *t2 = t1;
现在t2
和t1
指向同一个对象。完成对象后,只需执行以下操作:
delete t1;
(delete t2
会产生同样的效果)。现在该对象被销毁,但指针仍然指向同一个地址(这不再安全)。做
t2->doStuff();
在delete
调用未定义的行为之后。如果我们在删除之前回过头来,请考虑一下:
t1 = NULL;
t2 = NULL;
现在我们不再拥有我们创建的对象的地址,因此我们无法在其上调用delete
。这会造成内存泄漏。这有希望让您了解正在发生的事情。现在忘掉这一切,阅读RAII。
答案 1 :(得分:4)
只是声明一个指针不会分配内存来保存类,它不会调用构造函数。您必须调用new new来实际分配内存并提供对象初始化。此外,无需强制转换返回类型。
哦,我也不得不告诉你,在C ++中,除了手动管理内存之外,你应该总是寻找其他方法,这些是容器类(std :: vector,std :: deque等)和智能指针,这两者都使得管理内存变得更加痛苦。
答案 2 :(得分:2)
MyClass* t1;
这实际上并不为MyClass对象分配内存。你的变量只是一个可能跟踪MyClass对象的内存地址的指针,但是还没有创建这样的对象,并且指针没有被设置为指向任何地方。
(当然,为指针本身分配了一些内存,但是如果这个语句在函数内部,那就是在堆栈上,否则就是全局的。)
t1 = (MyClass*)new MyClass;
这是创建MyClass实例的正确思路。不过,最好的方法通常是在堆栈中进行:
MyClass t; // who needs a pointer?
然后你甚至不必考虑记忆。缺点是该对象仅存在,直到您离开创建它的范围,如{和}的嵌套所示。
如果你想让对象活得更长,那么你确实需要它在堆上(或者可以使它成为一个静态/全局变量)。对于堆上的动态分配,只需使用:
t1 = new MyClass;
你不需要 - 或者想要 - 显式地将返回的指针强制转换为MyClass * ...如果你在一个地方而不是另一个地方更改了类名,它只是多余的并且是潜在的错误来源。
一段时间后,你也想要删除t1。
MyClass的实际大小可能不是9个字节..它取决于编译器,可能是编译器命令行标志,编译器版本,目标内存模型,操作系统等的函数。
答案 3 :(得分:2)
除了其他一些答案:
课程的“大小”受到许多因素的影响:
虽然演示是不必要的,但它也是糟糕的C ++风格。不想使用C风格的强制转换,而是使用C ++中其他更安全(或至少更明确)的强制类型转换:
dynamic_cast<type>()
static_cast<type>()
reinterpret_cast<type>()
答案 4 :(得分:2)
正如许多人所说,MyClass
的大小取决于实现。在这种情况下,因为类没有方法,所以你基本上有一个结构,所以可以对大小做一些合理的猜测。在没有任何异常编译器标志的普通现代32位机器上,结构的大小将为12个字节;这是因为默认情况下,字段在当前体系结构上与4字节边界对齐。
在64位计算机上它可能更大,但如果它大于24字节(即每个字段的8字节对齐),我会有点惊讶。除非明确告知,否则我认为任何事情都不会使用任何大于8字节对齐的字段,并且对字段使用较大的对齐值没有多大意义,因为内存分配函数本身通常具有8字节对齐。 (注意:不要指望你的机器是真的!)
实际知道任何内容大小的唯一方式是使用sizeof(MyClass)
。您几乎不需要在C ++中使用它,因为new
运算符会为您了解并分配所需的空间量。如前所述,请记住,任何(char
除外)的大小都不可移植,即使它们实际上没有无偿变化。
执行MyClass *t1;
不会分配任何内容。它只是为您提供存储对象地址的位置(特别是MyClass
实例)。默认情况下,如果变量位于类或结构定义中的任何本地范围或中,则该空间指向la-la land。如果您不打算在变量中放置一个地址,那么将它显式初始化为NULL
可能是个好主意,这样它至少会指向一个明确的非对象。
您的t1 = (MyClass *)new MyClass;
包含不必要的强制转换,因为new
无论如何都会返回指向该类型对象的指针。做t1 = new MyClass;
就足够了。
如果t1
以前指向一个对象并且是唯一指向它的变量,那么就会出现内存泄漏(假设您没有使用垃圾收集器库;大多数C ++程序都是不使用它们而写的)。如果其他东西指向对象,那么最好承担清理它的责任。如果地址没有指向任何特定的地址,或者它指向NULL
,则没有任何内容泄露。
请记住,地址不会泄漏;物体泄漏。
您可以通过在堆栈上创建对象(使用直接MyClass t1;
)并通过引用而不是地址传递它们来缓解内存泄漏;当对象超出范围时,将自动删除该对象。当你有一个对象的生命周期不能很好地耦合到特定范围时,这样做的主要缺点就出现了。那是你使用指针(或智能指针,以某些限制为代价隐藏大多数的细节)。真正复杂的代码在垃圾收集方面更好,尽管它有自己的权衡(特别是包括更有可能增加内存消耗;这就是为什么Java比C ++更需要内存的核心原因)。
答案 5 :(得分:1)
不要假设MyClass使用9个字节,它取决于机器和编译器!
MyClass * t1;
这将为您提供一个可用的指针,但尚未分配持有类的空间。所以前两个问题的答案是否定的。
是的,如果你想使用指针,你必须为你自己分配空间。当然,您可以通过在堆*上创建MyClass来消除内存分配:
MyClass t1();
当t1超出范围时,该内存将自动释放。
答案 6 :(得分:1)
如果您执行类似
的操作MyClass *t1;
你只是声明一个指向MyClass类的指针。你并没有真正分配任何内存。要创建该类的实例,您可以使用以下任何一个:
MyClass t2; // this calls a default constructor implicitly
MyClass t3 = MyClass(); // this also calls a default constructor explicitly
MyClass *t4 = new MyClass; // calls default constructor implictly
两个第一个声明使用自动存储,而最后一个声明使用动态存储。如果为类定义参数化构造函数,则声明将如下所示:
MyClass t5(arg1, arg2, arg3);
MyClass t6 = MyClass(arg1, arg2, arg3);
MyClass *t7 = new MyClass(arg1, arg2, arg3);
答案 7 :(得分:1)
- 无法保证此类对象占用9个字节。这完全是特定于实现的行为
- MyClass *ptr
只是声明一个指向'MyClass'类型的指针。它还没有指向“MyClass”类型的任何对象。如果它是全局的,它将被初始化为零,否则如果这样的指针是本地的(例如函数范围),它将是未初始化的。
- 您需要初始化此指针以指向“MyClass”对象
e.g。假设'm'是'MyClass'类型的对象
MyClass m;
ptr = &m; // this does not create any new object(no constructor runs)
OR
MyClass *ptr;
ptr = new MyClass(); // This new expression, allocates memory large
// enough to hold a 'MyClass' object, initializes
// the object by running it's constructor
delete ptr; // delete the MyClass object by running it's
// destructor, return the allocated memory back
// to the implementation
- 如果在执行新操作后没有删除指针'ptr',那肯定是内存泄漏
答案 8 :(得分:0)
该类可能不是9个字节。如果编译器通过填充结构来使其更好地适应计算机体系结构,则可能会更多。
MyClass * t1;没有给你一个可用的地址。它是一个未初始化的指针,指向一个随机的内存位置,并且无法确定该地址的内容。它不会为您分配任何空间来存储MyClass实例,也不会调用构造函数。我建议您在定义指针时初始化指针:
MyClass *t1 = 0; // use 0, NULL, or null_ptr
您需要为类保留空间并调用构造函数。 'new'为你做这两件事。
MyClass *t1 = new MyClass();
不要忘记你需要一个'删除'来匹配每个新的,否则你将写入内存泄漏。