听起来您正在从 C# 之类的托管语言接近 C++。C++ 中的情况有些不同。
您描述为引用类型的内容在 C++ 中不存在。C++ 中的类型不是“引用类型”,也不是“值类型”。它们只是“类型”。它们是通过引用(或指针)还是通过值处理完全取决于使用类型的代码(而不是类型的定义)。相比之下,在 C# 等语言中,类型声明器决定是否必须将类型作为引用或值来处理。C++ 确实有一个叫做引用的东西,但它与你描述的东西无关。我将在最后提到 C++ 参考。
现在,让我们看看我们是否可以处理您问题的几个部分:
将指针放在堆栈上,然后创建指针指向的实际数据并将其放置在堆上。
也许。如果您像这样创建对象,那将是正确的,例如:
class MyClass { /* ... */ };
...
MyClass* pObj1 = new MyClass();
MyClass* pObj2 = (MyClass*)malloc( sizeof(MyClass) );
但如果您像这样创建对象,则不会:
MyClass obj3;
在后者中,对象在堆栈中分配,不涉及指针或引用。您正在将其作为“值类型”进行操作。
MyClass *pObj3 = &obj3;
NowpObj3
是指向 的指针(在堆栈中)obj3
,它也在堆栈中。看?类定义与该类对象的存储位置之间没有联系。这取决于您如何使用类型。具有相同类型的基于堆栈和堆的对象的组合是很常见的。
标准数组和用户定义的类是引用类型。这个对吗?
不; 数组只是一组放置在连续内存位置的相同类型/大小的对象。数组可以分配在堆栈或堆中,就像单个对象一样。
C 和 C++ 没有在数组上放置任何不同的语义(有一个例外,我稍后会提到)。一旦它们被分配,它们只是一堆碰巧是连续的对象。使用数组操作或直接指针操作来访问各个成员取决于您的程序。这意味着:
arrayOfClass[i]
和说完全一样((Class*)*(array + i))
。在 C 语言中,您甚至可以说 toi[arrayOfClass]
并且它的含义与arrayOfClass[i]
(但 C++ 会抱怨,因为它具有更严格的类型规则)。
- 您可以在指向不属于数组的对象的指针中使用数组运算符(并且可能会崩溃)
- 您可以对数组上的元素使用普通的指针操作。
- 您可以通过将较小的连续内存片段解释为好像它们是较小对象数组的成员(这正是您使用 malloc() 时得到的结果)来分配“大”内存块并“制作自己的数组”。
数组本身不是类型。它们只是分配多个对象的一种便捷方式,并且是一种在某些情况下更方便的指针方式。
我想到这个“数组不是特殊的”规则的唯一例外是在 C++ 中通过new
. 当您通过 分配数组时new
,它会留下有关分配数组时包含多少元素的信息(通常在与数组相邻的堆中,但这不是强制性的)。然后,您必须使用特殊delete []
运算符来删除该数组。delete []
找到并使用额外的信息来正确删除数组的所有元素。
其次,我的主要问题是 c 和 c++ 的内存管理机制(malloc、free 和 new、delete)是否总是正确处理并释放类或数组指向的内存?
只要你做事正确,是的。
如果这些指针以某种方式重新分配给堆上相同大小/类型的其他对象,一切仍然有效吗?
是的free()
,尽管在调用 free() (而不是 a )时使用指向不同类型的指针void*
是一件相当不寻常的事情。这些东西有合法用途,但它们是高级主题。您可能希望让经验丰富的开发人员查看您的设计,看看它是否真的适合做。
delete
是另一回事;如果您在调用 delete 时使用指向与缓冲区中存储的类型不同的类型的指针,则行为是“未定义的”(也就是您可能会崩溃)。那是因为delete
做的比做的多free()
;它还调用对象的析构方法,编译器依赖指针的类型来调用正确的方法。如果你使用了错误的指针类型,就会调用错误的方法,谁知道会发生什么。您可以在 d 之后将“其他”内容放入缓冲区中new
,但这可能需要大量的工作,这又是一个高级主题。
另请注意,您永远不应该分配 withmalloc()
和 free with delete
,也不应该分配 withnew
和 free with free()
。确保您的方法正确配对。
如果一个类有一个指向另一个对象的指针成员怎么办?我假设删除/释放类对象不会释放它的成员指针指向的东西,对吗?
在 C++ 中,处理这个问题的规范方法是类应该有一个析构函数,而析构函数会负责释放指针成员。在 C 中,您别无选择,必须在清除外部指针之前手动清除指针成员。
这一切都假设对象拥有成员指针所指向的内容。在像 C# 这样的托管语言中,所有对象都由运行时“拥有”并在垃圾收集器的控制下被删除,因此您不必担心。在 C++ 中。谁“拥有”成员指针指向的对象是由程序的语义定义的,而不是语言,您必须注意确定何时是删除嵌入对象的正确时间。如果你错过了删除对象的正确时间,你就会泄漏内存;如果你过早删除它,你会得到未定义的行为和崩溃(当一些代码试图使用已经被删除的对象时)。
现在,C++引用基本上只是一个带有一点糖衣的指针,旨在使某些类型的指针更易于使用。原则上,在 C++ 中仅使用指针就无法使用引用,几乎没有什么可以做的。少数例外是我将跳过的高级主题(我必须查找它以使主题公正,而且我手头没有资源)。
从您的角度来看,C++ 引用只是一个看起来像堆栈对象的指针。
CMyClass pObj1 = new CMyClass();
CMyClass& myObject = pObj1; // Create a reference to the object pointed by pObj1
pObj1->Method1(); // Use the pointer to call Method1
pObj1.Method1(); // Use the reference to call Method1
鉴于您在 C++ 方面的知识水平,我现在可能会远离参考资料,直到您更好地理解 C/C++ 中的内存管理。