C++引用类型的内存管理
我仍然是一个相当新手的程序员,我有一个关于引用类型的 C++ 内存管理的问题。
首先,我对引用类型的理解:
指针放在栈上,指针指向的实际数据被创建并放在堆上。标准数组和用户定义的类是引用类型。这是正确的吗? 其次,我的主要问题是 c 和 c++ 的内存管理机制(malloc、free 和 new、delete)总是正确处理这个问题并释放类或数组指向的内存吗?如果这些指针以某种方式重新分配给堆上相同大小/类型的其他对象,一切仍然有效吗?如果一个类有一个指向另一个对象的指针成员怎么办?我假设删除/释放类对象不会释放它的成员指针指向的内容,这是正确的吗?
谢谢大家!
-R
I'm still a fairly novice programmer and I have a question about c++ memory management with refence types.
First of all, my understanding of reference types:
A pointer is put on the stack and the actual data that the pointer points to is created and placed on the heap. Standard arrays and user defined classes are refence types. Is this correct?
Second, my main question is do c and c++'s memory management mechanisms (malloc, free and new, delete) always handle this properly and free the memory that a class or array is pointing to? Does everything still work if those pointers get reassigned somehow to other objects of the same size/type on the heap? What if a class has a pointer member that points to another object? I am assuming that delete/freeing the class object doesn't free what it's member pointer points to, is that correct?
Thanks all!
-R
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
听起来您正在从 C# 等托管语言转向 C++。 C++ 中的情况有些不同。
C++ 中不存在您所描述的引用类型。 C++ 中的类型不是“引用类型”,也不是“值类型”。他们只是“类型”。它们是通过引用(或指针)还是通过值处理完全取决于使用类型的代码(而不是类型的定义)。相比之下,在 C# 等语言中,类型声明器决定类型是否必须作为引用或值处理。 C++ 确实有一个叫做引用的东西,但它与你描述的东西无关。我会在最后提到 C++ 参考资料。
现在,让我们看看是否可以处理您问题的几个部分:
或许。例如,如果您像这样创建对象,则情况会如此:
但如果您像这样创建对象,则情况并非如此:
在后者中,对象在堆栈中分配,并且不涉及指针或引用。您将其作为“值类型”进行操作。
现在,
pObj3
是一个指向obj3
的指针(在堆栈中),它也在堆栈中。看?类定义和该类对象的存储位置之间没有联系。这取决于您如何使用该类型。具有相同类型的基于堆栈和堆的对象的组合是很常见的。不;数组只是放置在连续内存位置的一组相同类型/大小的对象。数组可以在堆栈或堆中分配,就像单个对象一样。
C 和 C++ 不会在数组上放置任何不同的语义(除了一个例外,我稍后会提到)。一旦它们被分配,它们只是一堆碰巧连续的对象。由您的程序使用数组操作或直接指针操作来访问各个成员。这意味着:
arrayOfClass[i]
与((Class*)*(array + i))
完全相同。在 C 中,您甚至可以说i[arrayOfClass]
,它的含义与arrayOfClass[i]
相同(但是 C++ 会抱怨,因为它有更严格的类型规则)。数组本身并不是类型;它们只是分配多个对象的一种方便方法,也是一种在某些情况下更方便的指针处理方法。
我想到的“数组并不特殊”规则的唯一例外是在 C++ 中通过 new 分配数组的情况。当您通过 new 分配数组时,它会留下有关分配数组时包含多少元素的信息(通常在与数组相邻的堆中,但这不是强制性的)。然后,您必须使用特殊的
delete []
运算符来删除该数组。delete []
查找并使用该额外的信息来正确删除数组的所有元素。只要你做事正确,是的。
对于
free()
是的,尽管在调用 free() 时使用指向不同类型的指针(除了void*
)是一件相当不寻常的事情。这些东西有合法的用途,但它们是高级主题。您可能希望让经验丰富的开发人员检查您的设计,看看它是否真的合适。删除
是另一回事;如果您使用的指针与调用delete时存储在缓冲区中的类型不同,则行为是“未定义的”(也就是您可能会崩溃)。这是因为delete
的作用比free()
的作用更多;它还调用对象的析构函数方法,编译器根据指针的类型来调用正确的方法。如果使用错误的指针类型,就会调用错误的方法,谁知道会发生什么。在new
之后,您可以在缓冲区中放入一些“其他”内容,但这可能需要大量的工作,并且又是一个高级主题。另请注意,您永远不应该使用
malloc()
分配并使用delete
释放,也不应该使用new
分配并使用free 释放()
。确保您的方法正确配对。在 C++ 中,处理这个问题的规范方法是类应该有一个析构函数,并且析构函数将负责释放指针成员。在 C 中,您别无选择,必须在清除外部指针之前手动清除指针成员。
所有这些都假设对象拥有成员指针指向的内容。在 C# 等托管语言中,所有对象均由运行时“拥有”,并在垃圾收集器的控制下删除,因此您不必担心这一点。在C++中。谁“拥有”成员 pointes 指向的对象是由程序的语义而不是语言定义的,您必须注意决定何时是删除嵌入对象的正确时间。如果你错过了删除对象的正确时间,你就会泄漏内存;如果删除得太早,则会出现未定义的行为并崩溃(当某些代码尝试使用已删除的对象时)。
现在,C++ 引用基本上只是一个带有一点糖衣的指针,旨在使某些类型的指针更易于使用。原则上,在 C++ 中仅使用指针无法对引用执行的操作几乎没有。少数例外是我将跳过的高级主题(我必须查找它才能公正地对待该主题,而且我手头没有资源)。
从您的角度来看,C++ 引用只是一个看起来像堆栈对象的指针。
鉴于您在 C++ 方面的知识水平,我现在可能会远离参考资料,直到您更好地理解 C/C++ 中的内存管理。
Sounds like you are approaching C++ from a managed language like C#. Things are a bit different in C++.
What you describe as reference types does not exist in C++. Types in C++ are not 'reference types', nor are they 'value types'. They are just 'types'. Whether they are handled via references (or pointers) or by value depends entirely on the code that uses the type (not the definition of the type). By contrast, in languages like C#, the type declarer decides whether the type must be handled as reference or a value. C++ does have something called a reference but it has nothing to do with the things you describe. I'll mention C++ references at the end.
Now, let's see if we can process the several parts of your question:
Maybe. That would be true if you create the object like this, for example:
But not if you create the object like this:
In the latter, the object is allocated in the stack and there is no pointer or reference involved. You are manipulating it as a 'value type'.
Now
pObj3
is a pointer (in the stack) toobj3
, which is also in the stack. See? No connection between the class definition and where objects of that class are stored. It depends on how you use the type. It's pretty common to have a combination of both stack and heap based objects of the same type.No; arrays are just a set of objects of the same type/size placed in consecutive memory locations. The array can be allocated in the stack or in the heap, just like with individual objects.
C and C++ don't place any distinct semantic on arrays (with one exception I'll mention in a second). Once they are allocated, they are just a bunch of objects that happen to be consecutive. It's up to your program to use array operations or direct pointer operations to access the individual members. That means:
arrayOfClass[i]
is exactly identical to saying((Class*)*(array + i))
. In C, you can even say toi[arrayOfClass]
and it means the same asarrayOfClass[i]
(C++ however will complain because it has stricter type rules).Arrays are not types on their own right; they are just a convenient way to allocate multiple objects, and a way to do pointers that is more convenient in some circumstances.
The only exception that comes to my mind to this "arrays are not special" rule is the case arrays allocated in C++ via
new
. When you allocate an array vianew
, it leaves information (usually in the heap adjacent to the array, but that is not mandatory) about how many elements the array contained when it was allocated. Then, you must use the specialdelete []
operator to delete the array.delete []
finds and uses that extra bit of information to delete all of the elements of the array correctly.As long as you do things correctly, yes.
Yes for
free()
, although using pointers to a different type when you call free() (other than avoid*
) is a rather unusual thing to do. There are legitimate uses of such things, but they are advanced topics. You might want to have your designed looked at by an experienced developer to see if it really is an appropriate thing to do.delete
is a different matter; if you use a pointer to a type different from what's stored in the buffer at the time you call delete, the behavior is "undefined" (a.k.a. you'll likely crash). That's becausedelete
does more than whatfree()
does; it also calls the object's destructor method, and the compiler relies on the type of the pointer to call the proper method. If you use the wrong pointer type, the wrong method will be called and who knows what will happen. You can potentially put something "else" in the buffer after younew
'd it, but that might requires a non-trivial amount of work and is again an advanced topic.Also note that you should never allocate with
malloc()
and free withdelete
, nor should you allocate withnew
and free withfree()
. Make sure your methods are properly paired.In C++, the canonical way to deal with that is that the class should have a destructor, and the destructor would take care of freeing the pointer member. In C you don't have a choice and you have to clear the pointer member by hand before clearing the outside pointer.
That all assumes that the object owns the contents pointed by the member pointer. In managed languages like C#, all objects are 'owned' by the runtime and deleted under control of the garbage collector, so you don't have to worry about it. In C++. who 'owns' objects pointed by member pointes is defined by the semantics of your program, not the language, and you have to pay attention to decide when is the right time to delete embedded objects. If you miss the right time to delete the object, you leak memory; if you delete it too soon, you get undefined behavior and crashes (when some code tries to use the object that has already been deleted).
Now, a C++ reference is basically just a pointer with a bit of sugar-coating, intended to make certain kind of pointers easier to use. In in priciple, there is almost nothing that you can do with references that you cannot do in C++ by just using pointers; the few exceptions are advanced topics that I'll skip (I would have to look it up to give the topic justice and I don't have my resources at hand).
For your point of view a C++ reference is just a pointer that looks like a stack object.
Given your level of knowledge in C++ I might stay away from references for now, until you come to understand memory management in C/C++ a little better.
默认情况下这是正确的,这就是 C++ 类具有析构函数的原因。例如:
如果我删除
cptr
,则会出现内存泄漏,因为x
未释放。所以我将向C
添加一个析构函数:(你的问题的其余部分有很多问题。你似乎混淆了 Java 和 C++,这就是为什么你的大部分问题没有意义我只回答了这个片段,为您提供如何自动释放资源的示例,我建议您阅读一本 C++ 书籍以更好地理解该语言。)
That's correct by default, which is why C++ classes have destructors. For example:
If I delete
cptr
, there will be a memory leak sincex
isn't freed. So I will add a destructor toC
:(There are tons of issues with the rest of your question. You seem to have confused Java and C++, which is why most of your question doesn't make sense. I've answered only this fragment to give you an example of how to free resources automatically. I suggest you read a C++ book to get a better understanding of the language.)
我不知道从哪里开始。
我们有原始类型(例如 int)。
我们有我们的老朋友 c 结构。
我们有课。
所有这些都可以是存储类自动的;只是坐在堆栈上。一切都可以按值传递。
然后我们就有了指向 x 的指针。 (
x *
)。它们将项目的地址存储在堆栈上,或分配在其他位置,如new
。一旦你获得了一个指针,你就需要确保你不会做一些使其无效的事情。如果从函数返回,则指向其动态上下文中的自动项的指针将变得无效。如果您使用delete
,则您删除的指针将不再有效,当然,它的任何副本也不再有效。最后,我们有了参考资料。 x&只是语法糖。传递对某事物的引用只是传递一个指向它的指针。使用引用类型可以避免键入一些
*
字符,并且断言表下的指针永远不会为空。I'm not sure where to start.
We have primitive types (e.g. int).
We have our old friend the c struct.
We have classes.
All of these can be storage class automatic; just sitting on the stack. All can be passed by value.
Then we have pointer-to-x. (
x *
). These store the address of an item on the stack, or allocated somewhere else, as withnew
. Once you get a pointer, it's up to you to make sure that you don't do something that invalidates it. If you return from a function, pointers to automatic items in its dynamic context become invalid. If you usedelete
, the pointer you delete through is no longer valid, and, of course, neither are any copies of it.Then, finally, we have references. x& is just syntactic sugar. Passing a reference to something is just passing a pointer to it. Using a reference type avoids the need to type some
*
characters, and it asserts that the pointer-under-the-table will never be null.指针是存储另一个变量地址的变量。是的,可能就是这种情况。指针始终可以指向本地范围(或堆栈)或空闲存储(堆)。
例子 :
首先,尽量避免free和delete混用。其次,
free()
获取一个指针,但不检查提供的指针是否指向有效的内存位置。这意味着您可以尝试释放未分配的内存,这可能会导致段错误。一个标准的例子是指针的另一个错误使用可能是:
是的,事情可以继续工作,但这可能会导致内存泄漏。只要把旧的东西删掉就可以了。
free()
类不会调用析构函数,您可以使用析构函数来避免内存泄漏。使用delete
你可以设置析构函数来删除其他对象,否则会导致内存泄漏。你的帖子告诉我你混淆了一些东西,你开始谈论 C++ 中的引用,但最终谈论指针和
free()
和delete
,并且通常给人的印象是你很困惑。我认为你应该买一本好书。A pointer is variable which stores the address of another variable. Yes this could be the case. A pointer can always point to the local scope (or stack) or to the free store (heap).
Example :
First of all, try to avoid mixing free and delete. Second,
free()
gets a pointer and does not check if the pointer provided points to a valid memory location. This means you could try to free non-allocated memory which could cause Seg Faults. A standard example of this isAnother wrong use of a pointer could be:
yes things can continue working, but this could cause memory leeks. As long as you delete the old stuff, this is fine.
free()
ing the class does not call the Destructor, and you can use the destructor to avoid memory leaks. Usingdelete
you can set the destructor to delete that other object, otherwise it will cause memory leaks.Your post tells me that you confuse some stuff, you start saying about References in C++ but end up talking about pointers and
free()
anddelete
, and generally giving the impression that you are confused. I think you should get a good book.类不一定是引用类型。例如,如果您有以下代码:
那么在 main 内部,barry 和 x 将在堆栈上彼此相邻。因此堆栈将包含两个整数和一个指向字符的指针。
您说得对,释放对象并不会释放其成员指针所占用的内存。 C++ 不会为您执行任何自动内存管理,因此必须手动分配和释放所有内容。在这种情况下,最好给狗一个析构函数,
当你删除狗或狗超出范围并从堆栈中取出时,像~dog这样的东西就会被调用。
Classes aren't necessarily reference types. If for example you had the following code:
Then inside main, barry and x would be next to each other on the stack. So the stack would contain two integers and a pointer to a char.
You are right in saying that freeing the object doesn't free the memory its member pointers do. C++ doesn't do any automatic memory management for you so have to be allocate and free everything manually. In this case it'd be best to give dog a destructor, something like
~dog will get called when you delete a dog or when a dog falls out of scope and gets taken off the stack.
引用基本上是一个使用自动变量语法的常量指针。您无法更改它指向的内容,也不能使用它来删除对象(直接;您可以在引用上使用地址运算符来获取指向所引用对象的指针,然后删除该对象)。
函数
malloc
和free
只是分配和释放内存块。您通常不需要在 C++ 中直接使用它们。运算符new
和delete
分配和释放内存,但它们也调用对象的构造函数和析构函数。在类的构造函数中,所有自动成员的构造函数都会被调用,析构函数也是如此。如果您的类有一个成员是指针,则它的内存不会自动分配或释放。您必须在构造函数和析构函数中显式执行此操作,或者使用像 std::auto_ptr 这样的智能指针。当对指针使用删除运算符时,会调用该指针类型的析构函数,因此,如果您使用显式强制转换强制指针指向错误的类型,则在以下情况下将调用错误的析构函数:你删除它。
A reference is basically a constant pointer that uses the syntax of an automatic variable. You can't change what it points to, and you can't use it to delete an object (directly; you can use the address operator on a reference to get a pointer to the referred object, then delete that).
The functions
malloc
andfree
just allocate and free blocks of memory. You usually don't need to use these directly in C++. The operatorsnew
anddelete
allocate and free the memory, but they also call the object's constructor and destructor. In a class's constructor all the automatic members' constructors are called, and the same with destructors. If your class has a member that is a pointer, it's memory is not automatically allocated or freed. You have to explicitly do that in the constructor and destructor, or use a smart pointer like std::auto_ptr.When the
delete
operator is used on a pointer, the destructor for the pointer type is called, so if you force a pointer to point at the wrong type with explicit casting, then the wrong destructor will be called when you delete it.