简体   繁体   English

C ++内存分配

[英]C++ Memory Allocation

When using C++, if there is a class: 使用C ++时,如果有一个类:

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

this class uses total of 9 bytes at memory… so if i do something like: 这个类在内存中总共使用9个字节...所以,如果我做了类似的事情:

MyClass *t1;

This will give me a usable address for the Class, but will it allocate the 9 bytes? 这将为我提供Class的可用地址,但是它会分配9个字节吗? And will it call default constructor? 它会调用默认构造函数吗? Or do I need to malloc those 9 bytes to the class? 或者我需要将这9个字节malloc给该类吗? If then I called something like: 如果那时我打电话给:

t1 = (MyClass *)new MyClass;

will it be considered memory leak? 是否会被视为内存泄漏? In other words, what happens to the old address? 换句话说,旧地址会发生什么?

  1. Do not make assumptions about the size of data-types, they are implementation dependent. 不要假设数据类型的大小,它们是依赖于实现的。
  2. MyClass *t1 defines an uninitialized pointer. MyClass *t1定义了一个未初始化的指针。 Dereferencing it invokes undefined behavior . 取消引用它会调用未定义的行为
  3. t1 = (MyClass *)new MyClass; allocates memory on the heap and creates a new object. 在堆上分配内存并创建一个新对象。 If this memory does not get deallocated using delete , there will be a memory leak. 如果此内存未使用delete取消分配,则会出现内存泄漏。 Also, you do not need the cast there, t1 = new MyClass(); 此外,你不需要在那里演员, t1 = new MyClass(); suffices. 就足够了。

Edit: About allocation. 编辑:关于分配。

MyClass *t1 = NULL; 

declares a pointer to a MyClass -object, but it does not create the object. 声明一个指向MyClass -object的指针,但它不会创建该对象。 This pointer is initialized to point to 0 . 该指针初始化为0 Now when you do 现在当你这样做

t1 = new MyClass();

the operator new creates a new instance of MyClass and assigns the address of that object to t1 . operator new创建一个新的MyClass实例,并将该对象的地址分配给t1 You can now work with that object through t1 : 您现在可以通过t1使用该对象:

t1->doStuff();

You can even create more pointers that point to the same object: 您甚至可以创建指向同一对象的更多指针:

MyClass *t2 = t1;

Now t2 and t1 point to the same object. 现在t2t1指向同一个对象。 When you are done with the object, just do: 完成对象后,只需执行以下操作:

delete t1;

( delete t2 would have the same effect). delete t2会产生相同的效果)。 Now the object is destroyed, but the pointers still point to the same address (which is not safe anymore). 现在该对象被销毁,但指针仍然指向同一个地址(这不再安全)。 Doing

t2->doStuff();

after the delete invokes undefined behavior. delete调用未定义的行为。 If we go back to before the delete, consider this: 如果我们在删除之前返回,请考虑以下事项:

t1 = NULL;
t2 = NULL;

Now we do not have the address of the object we created anymore, so we cannot call delete on it. 现在我们没有我们创建的对象的地址,所以我们不能在它上面调用delete This creates a memory leak. 这会造成内存泄漏。 This hopefully gives you some understanding of what is going on. 这有希望让您了解正在发生的事情。 Now forget all this and read about RAII . 现在忘掉这一切,阅读有关RAII的内容

Just declaring a pointer will not allocate memory to hold the class, and it will not call a constructor. 只是声明一个指针不会分配内存来保存类,它不会调用构造函数。 You have to call the operator new to actually allocate memory and provide object initialization. 您必须调用new new来实际分配内存并提供对象初始化。 Also, there is no need to cast the return type of new. 此外,不需要转换new的返回类型。

Oh, and I'm also obliged to tell you that in C++, you should almost always look for other ways than manually managing memory, these being container classes (std::vector, std::deque and the like) and smart pointers, both of which make managing memory a lot less painful. 哦,我也不得不告诉你,在C ++中,除了手动管理内存之外,你应该总是寻找其他方法,这些是容器类(std :: vector,std :: deque之类)和智能指针,这两者都使得管理内存变得更加痛苦。

MyClass* t1;

This doesn't actually allocate the memory for a MyClass object. 这实际上并不为MyClass对象分配内存。 Your variable is only a pointer with the potential to track the memory address of MyClass objects, but no such object is being created yet, and the pointer isn't being set to point anywhere. 您的变量只是一个可能跟踪MyClass对象的内存地址的指针,但尚未创建此类对象,并且指针未设置为指向任何位置。

(Of course, some memory is allocated for the pointer itself, but that's on the stack if this statement is inside a function, or global otherwise.) (当然,为指针本身分配了一些内存,但是如果这个语句在函数内部,那么它就在堆栈上,否则就是全局的。)

t1 = (MyClass*)new MyClass;

This is the right kind of thinking for creating a MyClass instance. 这是创建MyClass实例的正确思路。 Still, the best way is usually to do it on the stack: 不过,最好的方法通常是在堆栈上执行:

MyClass t;  // who needs a pointer?

Then you don't have to even think about the memory. 那你甚至不必考虑记忆。 The disadvantage is that the object only exists until you leave the scope it was created in, as indicated by the nesting of { and }. 缺点是对象只存在,直到你离开它创建的范围,如{和}的嵌套所示。

If you want the object to live longer then you really do need it on the heap (or can make it a static / global variable). 如果你希望对象活得更长,那么你确实需要它在堆上(或者可以使它成为一个静态/全局变量)。 For dynamic allocation on the heap just use: 对于堆上的动态分配,只需使用:

t1 = new MyClass;

You don't need - or want - to explicitly cast the returned pointer to MyClass*... it's just redundant and a potential source of bugs if you change the class name in one place but not another. 你不需要 - 或者想要 - 显式地将返回的指针强制转换为MyClass * ...如果你在一个地方而不是另一个地方更改了类名,它只是多余的并且是潜在的错误来源。

Some time later, you'll want to delete t1 too. 一段时间后,你也想要删除t1。

The actual size of MyClass may not be 9 bytes.. it depends on the compiler, and may be a function of compiler command line flags, compiler version, target memory model, OS etc.. MyClass的实际大小可能不是9个字节..它取决于编译器,可能是编译器命令行标志,编译器版本,目标内存模型,操作系统等的函数。

In addition to some of the other answers: 除了一些其他答案:

  1. The "size" of a class is influenced by many things: 一个类的“大小”受到许多因素的影响:

    • The implementation-defined size of the member variables 实现定义的成员变量大小
    • The vtable vtable
    • Memory alignment requirements of the member variables 成员变量的内存对齐要求
    • Padding 填充

  2. While the cast is unnecessary, it's also bad C++ style. 虽然演员阵容是不必要的,但它也是糟糕的C ++风格。 Prefer not to use the C-style cast, but use one of the other, safer (or at least more explicit) casts from C++: 不希望使用C风格的强制转换,而是使用C ++中另一个更安全(或至少更明确)的强制转换:

    • dynamic_cast<type>()
    • static_cast<type>()
    • reinterpret_cast<type>()

For more information see C++ Reference Guide - New C++ Cast Operators 有关更多信息,请参阅C ++参考指南 - 新C ++ Cast操作符

  1. As many people have said, the size of MyClass is implementation-dependent. 正如许多人所说, MyClass的大小取决于实现。 In this case, because the class has no methods, you've basically got a struct so it is possible to make some reasonable guesses as to size. 在这种情况下,因为类没有方法,所以你基本上有一个结构,所以可以对大小做一些合理的猜测。 On a normal modern 32-bit machine without any unusual compiler flags, the size of the structure will be 12 bytes; 在没有任何异常编译器标志的普通现代32位机器上,结构的大小将为12个字节; this follows from the fact that fields are aligned to 4-byte boundaries by default on current architectures. 这是因为默认情况下,字段在当前体系结构上与4字节边界对齐。

    On a 64-bit machine it could be even larger, but I'd be a bit surprised if it was bigger than 24 bytes (ie, 8-byte alignment for each field). 在64位机器上它可能更大,但如果它大于24字节(即每个字段的8字节对齐),我会有点惊讶。 I don't think anything uses anything larger than 8-byte alignment for fields unless explicitly told to, and there's not much point in using larger alignment values for fields as the memory allocation functions themselves usually have 8-byte alignment. 除非明确告知,否则我认为任何事情都不会使用任何大于8字节对齐的字段,并且对字段使用较大的对齐值没有多大意义,因为内存分配函数本身通常具有8字节对齐。 (NB: Don't count on that being true for your machine!) (注意:不要指望你的机器是真的!)

    The only way to actually know the size of anything is to use sizeof(MyClass) . 实际知道任何东西大小的唯一方法是使用sizeof(MyClass) You hardly ever need to use that in C++, as the new operator knows about that for you and allocates the amount of space needed. 您几乎不需要在C ++中使用它,因为new运算符为您了解并分配所需的空间量。 And as previously noted, remember that sizes of anything (other than char ) are not portable, even if they don't actually vary gratuitously. 并且如前所述,请记住, 任何东西 (除了char )的大小都不可移植,即使它们实际上没有无偿变化。

  2. Doing MyClass *t1; MyClass *t1; doesn't allocate anything. 不分配任何东西。 It just gives you a place to store the address of an object (specifically, a MyClass instance). 它只是为您提供了存储对象地址(特别是MyClass实例)的位置。 By default, that space points off into la-la land if the variable is in any local scope or in a class or structure definition. 默认情况下,如果变量位于任何局部范围类或结构定义中,则该空间指向la-la land。 If you're not about to put an address in the variable, it's probably a good idea to explicitly initialize it to NULL so that it at least points to a definite not-an-object. 如果您不打算在变量中放置一个地址,那么将它显式初始化为NULL可能是一个好主意,这样它至少可以指向一个明确的非对象。

  3. Your t1 = (MyClass *)new MyClass; 你的t1 = (MyClass *)new MyClass; contains an unnecessary cast, since new returns a pointer to an object of that type anyway. 包含一个不必要的强制转换,因为new返回一个指向该类型对象的指针。 Doing t1 = new MyClass; t1 = new MyClass; is enough. 足够。

  4. If t1 was previously pointing to an object and was the only variable pointing to it, you'll have a memory leak (assuming you've not used a garbage collector library; most C++ programs are written without using them). 如果t1以前指向一个对象并且是唯一指向它的变量,那么你将有内存泄漏(假设你没有使用垃圾收集器库;大多数C ++程序都是在不使用它们的情况下编写的)。 If something else is pointing to the object, then that had better be assuming the responsibility for cleaning it up. 如果其他东西指向对象,那么最好承担清理它的责任。 If the address isn't pointing to anything in particular, or it was pointing to NULL , then nothing is leaked. 如果地址没有指向任何特定的地址,或者它指向NULL ,则没有任何泄漏。

    Remember, addresses don't leak; 请记住,地址不会泄漏; objects leak. 物体泄漏。

    You can mitigate memory leaks by creating the objects on the stack (with straight MyClass t1; ) and passing them around by reference rather than address; 您可以通过在堆栈上创建对象(使用直接的MyClass t1; )并通过引用而不是地址传递它们来缓解内存泄漏; the object will then be deleted automatically when it passes out of scope. 当对象超出范围时,将自动删除该对象。 The main disadvantage of this comes when you have an object whose lifetime can't be coupled nicely to a particular scope. 当你有一个对象的生命周期不能很好地耦合到特定范围时,这样做的主要缺点就出现了。 That's when you use pointers (or smart pointers, which hide most of the details at a cost of some restrictions). 那是你使用指针(或智能指针,它以一些限制为代价隐藏大部分细节)。 Really complex code is just better with garbage collection, though that has its own trade-offs (notably including being much more likely to increase memory consumption; this is the core reason why Java is known for being more memory hungry than C++). 真正复杂的代码对于垃圾收集更好,尽管它有自己的权衡(特别是包括更有可能增加内存消耗;这是Java因为比C ++更多的内存需求而闻名的核心原因)。

Don't assume that MyClass uses 9 bytes, it depends by the machine and by the compiler! 不要以为MyClass使用9个字节,它取决于机器和编译器!

MyClass *t1; MyClass * t1;

That will give you a usable pointer , but the space to hold class has not been allocated. 这将为您提供一个可用的指针 ,但尚未分配持有类的空间。 So the answer to the first two questions is NO. 所以前两个问题的答案是否定的。

And yes, you have to allocate space for the class for yourself, if you want to use the pointer. 是的,如果你想使用指针,你必须自己为类自己分配空间。 Of course you could get rid of memory allocations by creating MyClass on the heap*: 当然,你可以通过在堆*上创建MyClass来摆脱内存分配:

MyClass t1 (); MyClass t1();

And that memory will be automatically released when t1 goes out of scope. 当t1超出范围时,该内存将自动释放。

  • I meant: stack. 我的意思是:堆栈。

If you do something like 如果你做的事情

MyClass *t1;

you are just declaring a pointer to a class MyClass. 你只是声明一个指向MyClass类的指针。 You are not really allocating any memory. 你并没有真正分配任何内存。 In order to create an instance of that class you can use any of these: 要创建该类的实例,您可以使用以下任何一个:

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

the two first declarations use automatic storage whilst the last one uses dynamic storage. 两个第一个声明使用自动存储,而最后一个声明使用动态存储。 If you define a parameterized constructor for your class the declarations would be something like this: 如果为类定义参数化构造函数,则声明将如下所示:

MyClass t5(arg1, arg2, arg3);
MyClass t6 = MyClass(arg1, arg2, arg3);
MyClass *t7 = new MyClass(arg1, arg2, arg3);

-There is no guarantee that this class object will occupy 9 bytes. - 无法保证此类对象占用9个字节。 This is completely implementation specific behavior 这是完全特定于实现的行为

- MyClass *ptr just declares a pointer to 'MyClass' type. - MyClass *ptr只是声明一个指向'MyClass'类型的指针。 It does not point to any object of type 'MyClass' yet. 它还没有指向“MyClass”类型的任何对象。 If it is global it will be zero initialized, else if such a pointer is local (eg function scope) it will be uninitialized. 如果它是全局的,它将被初始化为零,否则如果这样的指针是本地的(例如函数范围),它将是未初始化的。

-You need to initialize this pointer to point to a 'MyClass' object - 您需要初始化此指针以指向“MyClass”对象

eg assuming that 'm' is an object of type 'MyClass' 例如,假设'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

-If you don't delete the pointer 'ptr' after doing a new, it is a memory leak for sure - 如果在执行新操作后没有删除指针'ptr',那肯定是内存泄漏

The class may not be 9 bytes. 该类可能不是9个字节。 It could be more if the compiler rounds up by padding the structure to make it fit the computers architecture better. 如果编译器通过填充结构来使其更好地适应计算机体系结构,则可能会更多。

MyClass *t1; MyClass * t1; does not give you a usable address. 没有给你一个可用的地址。 It is an uninitialized pointer that points to a random memory location, and there's no telling what could be at that address. 它是一个未初始化的指针,指向一个随机的内存位置,并且无法确定该地址的内容。 It does not allocate you any space to store a MyClass instance and will not call a constructor. 它不会为您分配任何空间来存储MyClass实例,也不会调用构造函数。 I recommend you initialize pointers when you define them: 我建议您在定义指针时初始化指针:

MyClass *t1 = 0;   // use 0, NULL, or null_ptr

You need to reserve space for the class and call the constructor. 您需要为类保留空间并调用构造函数。 'new' does both of these for you. 'new'为你做这两件事。

MyClass *t1 = new MyClass();

Don't forget you need a 'delete' to match every new otherwise you'll be writing a memory leak. 不要忘记你需要一个'删除'来匹配每一个新的,否则你将写入内存泄漏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM