C ++内存管理引用类型
我仍然是一个相当新手的程序员,我有一个关于c ++内存管理与refence类型的问题。
首先,我对参考类型的理解:
指针放在堆栈上,并且指针指向的实际数据被创建并放置在堆上。 标准数组和用户定义的类是refence类型。 它是否正确? 其次,我的主要问题是,c和c ++的内存管理机制(malloc,free和new,delete)总是能够正确处理这个问题,并释放类或数组指向的内存? 如果这些指针以某种方式重新分配给堆上相同大小/类型的其他对象,一切仍然有效? 如果一个类有一个指向另一个对象的指针成员呢? 我假设删除/释放类对象不释放它的成员指针指向的是,是正确的?
谢谢大家!
-R
听起来就像你从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;
现在pObj3
是一个指针(在栈)到obj3
,这也是在堆栈中。 看到? 类定义与该类的对象之间没有连接。 这取决于你如何使用类型。 将相同类型的基于堆栈和堆的对象组合在一起非常常见。
标准数组和用户定义的类是refence类型。 它是否正确?
没有; 数组只是放置在连续内存位置中的一组相同类型/大小的对象。 数组可以分配在堆栈中或堆中,就像单个对象一样。
C和C ++不会在数组中放置任何不同的语义(有一个例外,我会在第二次提到)。 一旦分配完毕,它们只是一堆碰巧是连续的对象。 这取决于您的程序使用数组操作或直接指针操作来访问单个成员。 这意味着:
arrayOfClass[i]
与((Class*)*(array + i))
完全相同。 在C中,你甚至可以对i[arrayOfClass]
,它意味着与arrayOfClass[i]
相同(但是C ++会抱怨,因为它有更严格的类型规则)。 数组本身不是类型的; 它们只是一种分配多个对象的便捷方法,并且是一种在某些情况下更方便使用指针的方法。
我认为这个“数组不是特别的”规则的唯一例外是通过new
在C ++中分配的数组。 当你通过new
分配一个数组时,它会在分配数组时包含有关数组包含的元素的数量(通常在堆的旁边,但这不是强制性的)。 然后,您必须使用特殊的delete []
运算符来删除该数组。 delete []
查找并使用该额外的信息位以正确删除阵列的所有元素。
其次,我的主要问题是,c和c ++的内存管理机制(malloc,free和new,delete)总是能够正确处理这个问题,并释放类或数组指向的内存?
只要你正确地做事,是的。
如果这些指针以某种方式重新分配给堆上相同大小/类型的其他对象,一切仍然有效?
是的free()
,尽管当你调用free()(除void*
)时使用指向不同类型的指针是一件非常不寻常的事情。 这些东西有合法用途,但它们是高级主题。 您可能希望让有经验的开发人员查看您的设计,看看它是否真的是一个合适的事情。
delete
是另一回事; 如果您在调用delete时使用与缓冲区中存储的类型不同的类型的指针,则行为为“未定义”(也许您会崩溃)。 这是因为delete
比free()
所做的更多; 它也调用对象的析构函数方法,编译器依靠指针的类型来调用正确的方法。 如果使用错误的指针类型,将会调用错误的方法,谁知道会发生什么。 您可能会在new
'd'之后将某些“其他”放在缓冲区中,但这可能需要一些不重要的工作量,并且再次成为高级主题。
另外请注意,您不应该使用malloc()
分配,并且免费使用delete
,也不应该使用free()
和free分配new
和免费的。 确保您的方法已正确配对。
如果一个类有一个指向另一个对象的指针成员呢? 我假设删除/释放类对象不释放它的成员指针指向的是,是正确的?
在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 ++中的内存管理更好一点。
我假设删除/释放类对象不释放它的成员指针指向的是,是正确的?
这是默认情况下正确的,这就是C ++类具有析构函数的原因。 例如:
class C {
int* x;
public:
C () : x (new int[10]) {} // constructor
};
C* cptr = new C; // a pointer to a C object
如果我删除cptr
,将会有内存泄漏,因为x
没有被释放。 所以我会添加一个析构函数给C
:
class C {
...
~C () {delete [] x;} // destructor
};
(其余问题有很多问题,你似乎混淆了Java和C ++,这就是为什么你的大部分问题没有意义,我只回答这个片段给你一个例子,我建议你阅读一本C ++书籍以更好地理解该语言。)
我不知道从哪里开始。
我们有原始类型(例如int)。
我们有我们的老朋友c结构。
我们有课。
所有这些都可以自动进行存储类别; 就坐在栈上。 所有可以通过价值传递。
然后我们有指向x的指针。 ( x *
)。 这些将项目的地址存储在堆栈中,或将其分配到其他地方,就像new
。 一旦你得到了一个指针,这是由你来确保你不做一些无效的事情。 如果从函数返回,则指向其动态上下文中的自动项目的指针将变为无效。 如果你使用delete
,你删除的指针不再有效,当然,它也没有任何拷贝。
然后,最后,我们有参考。 x&只是语法糖。 传递对某个东西的引用只是将一个指针传递给它。 使用引用类型避免了键入一些*
字符的需要,并且它断言表下的指针永远不会为空。