C ++内存分配

时间:2010-09-15 08:17:54

标签: c++ memory-management

使用C ++时, 如果有一个班级:

class MyClass
{
    char memory1bye;
    int memory4bytes;
    int another4bytes;
};

这个类在内存中总共使用了9个字节...所以如果我做了类似的事情:

MyClass *t1;

这将为我提供Class的可用地址,但它会分配9个字节吗?它会调用默认构造函数吗? 或者我需要将这9个字节malloc给该类吗? 如果那时我打电话给:

t1 = (MyClass *)new MyClass;

是否会被视为内存泄漏?换句话说,旧地址会发生什么?

9 个答案:

答案 0 :(得分:14)

  1. 不要假设数据类型的大小,它们是依赖于实现的。
  2. MyClass *t1定义了一个未初始化的指针。取消引用它会调用undefined behavior
  3. t1 = (MyClass *)new MyClass;在堆上分配内存并创建一个新对象。如果未使用delete取消分配此内存,则会出现内存泄漏。此外,您不需要在那里投射,t1 = new MyClass();就足够了。
  4. 修改关于分配。

    MyClass *t1 = NULL; 
    

    声明指向MyClass - 对象的指针,但它不会创建对象。该指针初始化为指向0。现在当你做

    t1 = new MyClass();
    

    运算符new创建MyClass的新实例,并将该对象的地址分配给t1。您现在可以通过t1

    使用该对象
    t1->doStuff();
    

    您甚至可以创建更多指向同一对象的指针:

    MyClass *t2 = t1;
    

    现在t2t1指向同一个对象。完成对象后,只需执行以下操作:

    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)

除了其他一些答案:

  1. 课程的“大小”受到许多因素的影响:

    • 成员变量的实现定义大小
    • vtable
    • 成员变量的内存对齐要求
    • 填充

  2. 虽然演示是不必要的,但它也是糟糕的C ++风格。不想使用C风格的强制转换,而是使用C ++中其他更安全(或至少更明确)的强制类型转换:

    • dynamic_cast<type>()
    • static_cast<type>()
    • reinterpret_cast<type>()
  3. 有关详细信息,请参阅C++ Reference Guide - New C++ Cast Operators

答案 4 :(得分:2)

  1. 正如许多人所说,MyClass的大小取决于实现。在这种情况下,因为类没有方法,所以你基本上有一个结构,所以可以对大小做一些合理的猜测。在没有任何异常编译器标志的普通现代32位机器上,结构的大小将为12个字节;这是因为默认情况下,字段在当前体系结构上与4字节边界对齐。

    在64位计算机上它可能更大,但如果它大于24字节(即每个字段的8字节对齐),我会有点惊讶。除非明确告知,否则我认为任何事情都不会使用任何大于8字节对齐的字段,并且对字段使用较大的对齐值没有多大意义,因为内存分配函数本身通常具有8字节对齐。 (注意:不要指望你的机器是真的!)

    实际知道任何内容大小的唯一方式是使用sizeof(MyClass)。您几乎不需要在C ++中使用它,因为new运算符会为您了解并分配所需的空间量。如前所述,请记住,任何char除外)的大小都不可移植,即使它们实际上没有无偿变化。

  2. 执行MyClass *t1;不会分配任何内容。它只是为您提供存储对象地址的位置(特别是MyClass实例)。默认情况下,如果变量位于类或结构定义中的任何本地范围中,则该空间指向la-la land。如果您不打算在变量中放置一个地址,那么将它显式初始化为NULL可能是个好主意,这样它至少会指向一个明确的非对象。

  3. 您的t1 = (MyClass *)new MyClass;包含不必要的强制转换,因为new无论如何都会返回指向该类型对象的指针。做t1 = new MyClass;就足够了。

  4. 如果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();

不要忘记你需要一个'删除'来匹配每个新的,否则你将写入内存泄漏。