Cocos2d-x开发中C++内存管理详解

2015年03月23日 16:41 0 点赞 0 评论 更新于 2025-11-21 18:20

由于在之前的内容中未对C++语言进行介绍,自然也没有说明C++的内存管理。为了更好地掌握Cocos2d-x中的内存管理机制,有必要先了解一些C++内存管理的基础知识。

C++内存管理是一个复杂的主题,如果要全面、系统地介绍,可能需要一本书的篇幅才能解释清楚。这里仅为大家介绍C++内存管理的基本用法。

内存分配区域

创建对象通常需要两个步骤:第一步,为对象分配内存;第二步,调用构造函数初始化内存。在第一步为对象分配内存时,有几个不同的分配区域可供选择,具体如下:

栈区域分配

栈内存分配运算内置于处理器的指令集中,因此效率很高。然而,其分配的内存容量有限。栈内存由处理器自动分配和释放,主要用于存放函数的参数值和局部变量的值等。在执行函数时,函数内局部变量的存储单元会在栈上创建,当函数执行结束时,这些存储单元会自动被释放。

堆区域分配

从堆上分配内存,也称为动态内存分配。这种分配方式由开发人员负责分配和释放,如果开发人员不手动释放,程序结束时将由操作系统回收。程序在运行时可以使用mallocnew申请任意大小的内存,开发人员需要自己决定何时使用freedelete释放内存。动态内存的生存期由开发人员控制,使用起来非常灵活,但也容易引发较多问题。

静态存储区域分配

该内存空间在程序的整个运行期间都存在,内存在程序编译时就已经分配好。它主要用于分配全局变量和静态变量。

动态内存分配

动态内存分配虽然最为灵活,但也存在较多问题,因此我们重点介绍动态内存分配。动态内存使用mallocnew分配内存,使用freedelete释放内存。其中,mallocfree是成对使用的,newdelete也是成对使用的。

1. mallocfree的使用

mallocfree是C/C++语言的标准库函数,在C语言中使用较为频繁。使用malloc创建对象时,不会自动调用构造函数来初始化内存;使用free释放对象时,也不会自动调用析构函数来清除内存。

以下是使用mallocfree分配和释放内存的示例代码:

#include <iostream>
using namespace std;

class MyObject {
public:
MyObject() {
cout << "call constructor." << endl;
}
~MyObject() {
cout << "call destructor." << endl;
}
void initialize() {
cout << "call initialization." << endl;
}
void destroy() {
cout << "call destroy." << endl;
}
};

int main() {
MyObject *obj = (MyObject *)malloc(sizeof(MyObject)); // 申请动态内存
obj->initialize();
//TODO
obj->destroy();
free(obj);
obj = NULL;
return 0;
}

上述代码声明了MyObject类,其中:

  • 第①行代码声明了构造函数。
  • 第②行代码声明了析构函数。
  • 第③行代码声明了初始化函数void initialize(),由于使用malloc分配内存时不会调用构造函数,因此需要通过调用该函数来初始化对象。
  • 第④行代码声明了清除函数void destroy(),在使用free释放对象内存时,通过调用该函数来清除对象的一些资源。

第⑤ - ⑧行代码对MyObject类进行测试:

  • 第⑤行代码MyObject *obj = (MyObject *)malloc(sizeof(MyObject))使用malloc函数分配内存,使用该函数需要指定对象的长度,并且malloc函数的返回值是void*,由于C++不允许将void*赋值给其他指针类型,所以需要进行强制类型转换。由于构造函数不能显式调用,因此需要第⑥行代码来初始化对象。
  • 第⑧行代码free(obj)用于释放obj对象的内存。在释放对象内存之前,第⑦行代码obj->destroy()会被调用,用于清除对象的一些资源,其作用类似于析构函数,但真正的析构函数~MyObject()并未被调用。

运行结果如下:

call initialization.
call destroy.

2. newdelete的使用

mallocfree不同,newdelete不是函数库,而是C++的运算符。new运算符能够完成创建对象的所有步骤(即:第一步,为对象分配内存;第二步,调用构造函数初始化内存),它会自动调用构造函数。示例代码如下:

MyObject *obj = new MyObject();

构造函数可以重载,根据用户传递的参数列表,new运算符会决定调用哪个构造函数来初始化对象。

new运算符的反操作运算符是deletedelete会先调用析构函数,再释放内存。示例代码如下:

delete obj;

需要注意的是,obj只能用于释放new创建的对象,不能用于释放malloc创建的对象。而且,在使用delete释放对象后,需要将对象指针obj设置为NULL,以防止出现“野指针”。

“野指针”是一种危险的情况,通常有两种情况会产生“野指针”:

  • 指针变量没有被初始化,其指向是随机的,并非为NULL。如果使用if语句判断,会被认为是有效指针。
  • 指针变量被free或者delete之后,它们只是将指针所指的内存释放掉,但并没有清除指针本身,此时指针指向的是“垃圾”内存。如果使用if语句判断,也会被认为是有效指针。

为了避免“野指针”,良好的编程习惯是在这两种情况下都将指针设置为NULL

以下是使用newdelete分配和释放内存的示例代码:

#include <iostream>
using namespace std;

class MyObject {
public:
MyObject() {
cout << "call constructor." << endl;
}
~MyObject() {
cout << "call destructor." << endl;
}
void initialize() {
cout << "call initialization." << endl;
}
void destroy() {
cout << "call destroy." << endl;
}
};

int main() {
MyObject *obj = new MyObject(); // 申请动态内存
//TODO
delete obj;
obj = NULL;
return 0;
}

同样是MyObject类,采用new分配内存,delete释放内存。程序运行时会自动调用构造函数和析构函数。

运行结果如下:

call constructor.
call destructor.

作者信息

menghao

menghao

共发布了 3994 篇文章