cocos2d-x CCArray使用中避免出现野指针问题

问题及现象

  

  此前,调试cocos2d-x + CocoStudio游戏程序过程中遇到一个运行时错误。通过调用堆栈来看,错误指针停在~CCNodeRGBA()。

分析1

  CCNodeRGBA是一个继承自CCNode的子类,其主要是增加了与结点透明度相关的属性控制功能。

  

class CC_DLL CCNodeRGBA : public CCNode, public CCRGBAProtocol

    CCNodeRGBA是CCSprite和Widget(即UIWidget)的直接父类。因此,出现错误对象应当与CCSprite和Widget对象及可能的子类对象有关。

分析2

具体的错误堆栈内容我就不截图了。从栈中层次分析,最可能是析构结点过程(一级级的析构,这是C++的析构规则,由子到父到祖父再到...)的最后产生了上述错误,大致可以估计出现了内存崩溃。

分析3

  既然问题初步定位在内存崩溃,那么很可能与指针使用相关。回想前段时间(及很早以前--其实这部分代码很早前就有了)感觉系统运行正常--当然是还在没有增加新近添加的功能之前。

  那么,怎么尽快分析出BUG?代码近5000行!好在绝大部分都已经运行起来,于是我从测试实例入手(不敢直接使用缩小范围法:即由原来的200个模块缩小(主要是通过注释屏蔽的方法)到想测试的100个模块)。GOOD!在最简单的操作测试中,新增加功能正常!这给了我极大鼓励!

  于是,我继续使用测试实例法,并且把新增加功能一个个测试(反复进行)。最终,范围逐渐定位在一个大致的实现算法上。

问题解决

  问题出在:C++野指针问题!相关代码如下:

//此前有声明:Card* Cells[4]={NULL};
Cells[col]= (Card*)TableauPiles[whichColumnForCC]->lastObject();
Card* t2=Cells[col];
Cells[col]->setPosition(m_pCurNode->getChildByTag(10006+col)->getPosition());
m_pCurNode->reorderChild(Cells[col],++zIndexForAll);
Cells[col]->setPosition(m_pCurNode->getChildByTag(10006+col)->getPosition());
m_pCurNode->reorderChild(Cells[col],++zIndexForAll);
TableauPiles[whichColumnForCC]->removeLastObject();

  问题出在第一句与最后一句。第一句的意义是指:把Cells[col]指针指向一个已经内存分配的Card对象所在位置。而最后一句,把这个对象对应内存释放掉了!于是Cells[col]指向的对象成了僵尸。

  僵尸很可怕!我们大家都看过类似体裁的故事吧!我的游戏中,Cells[col]指针的精灵仍然可以在屏幕上移动,好像是正常的精灵一样!!!这也正是我直接使用以前的代码而现在迟迟没有发现的原因之一(当然,根本原因还与本人C++功能有密切关系)。

进一步分析

  大家都知道CCArray这个数据类型很好使用,因为它继承自STL中现有结构,而且效率明确提高,特别是其提供了大量的简化数组操作的方法。但是,也有不足:在使用CCArray时,有关retain问题我就不再重复了--网络上有的是。在普通的cocos2d-x编码中,使用了retain那么在最后就要调用相应的release;但是,当你配合使用CocoStudio UI编辑器或者场景编辑器进行开发时就不需要(也不能够)调用release了。有关细节,Hanrea告诉我UI解析器在释放场景内容时自动处理了。当然,我还没有分析他们的源代码,所以只是这么结论了。当然,在我前段时间花费不少时间克服的问题也证明了这一点。

  接下来,removeLastObject这个方法有一个bool类型参数,默认为true,即释放掉相应内存。源码如下:

void CCArray::removeLastObject(bool bReleaseObj)
{
    CCAssert(data->num, "no objects added");
    ccArrayRemoveObjectAtIndex(data, data->num-1, bReleaseObj);
}

  再跟踪下去:

void ccArrayRemoveObjectAtIndex(ccArray *arr, unsigned int index, bool bReleaseObj/* = true*/)
{
    CCAssert(arr && arr->num > 0 && index < arr->num, "Invalid index. Out of bounds");
    if (bReleaseObj)
    {
        CC_SAFE_RELEASE(arr->arr[index]);
    }
    
    arr->num--;
    
    unsigned int remaining = arr->num - index;
    if(remaining>0)
    {
        memmove((void *)&arr->arr[index], (void *)&arr->arr[index+1], remaining * sizeof(CCObject*));
    }
}

  注意上面第一个if语句,当参数为true时,把相应内存真正释放掉了。请参考我们上面的游戏代码。

  最后,纠正一下错误,很简单,把上面最后一句修改如下即可:

TableauPiles[whichColumnForCC]->removeLastObject(false);

显然,传递参数false就是只在本数组中进行了下标调整(arr->num也比原来减小了1),并没有进行最后一个结点对应的内存释放。反而,最后一个数组结点对应内存被Cells[col]这个指针接收下了。于是,避免了一个野指针(僵尸)事件,一切自然OK!

时间: 2024-10-12 19:02:55

cocos2d-x CCArray使用中避免出现野指针问题的相关文章

C中的野指针—如何避免

转自:http://www.cnblogs.com/viviwind/archive/2012/08/14/2638810.html 先看一个例子: struct student{ char* name; int score;}stu, *pstu; int main(void){ strcpy(stu.name, "Jimy"); stu.score =99; return0;} 这是很多人都容易犯的错误:定义了结构体变量stu,但结构体内部的char * name在定义结构体时只是

21 野指针

分析下列程序,输出结果:不确定,未定义行为. #include<iostream> #include<stdlib.h> #include<string.h> using namespace std; void test(void) { char *str=(char *)malloc(100); strcpy(str,"hello"); free(str); if(str!=NULL) { strcpy(str,"world")

C语言指针2(空指针,野指针)

//最近,有朋友开玩笑问 int *p  *是指针还是p是指针还是*p是指针,当然了,知道的都知道p是指针 //野指针----->>>指没有指向一个地址的指针(指针指向地址请参考上一篇文章) //空指针---->>指向空(null)的指针就是空指针 //指针的其他用法,指针可以指向指针,指针可以进行+ - * /运算 /* 特别注意,各个编译器都不相同,这里有一种错误写法,如: int *p,int a=10,b=20; p=&b; *p = &a;    

野指针

参考:http://chenqx.github.io/2014/09/25/Cpp-Memory-Management/ 内存管理详解 野指针: 一.申请了指针没有初始化,全局指针未初始化编译时无错误,运行会出错,局部指针编译时会报错--unintialized: 二.malloc申请的内存用free(ptr)释放后,ptr指向的是垃圾内存,或者new出来的对象delete之后,指针所指的对象释放掉了,指针还存在,但指向的是垃圾内存,对待这种错误,可以在释放掉后把指针置为NULL: 三.函数返回

野指针的学习

1.野指针     野指针是指向了"垃圾"内存的指着. 2.产生原因     (1)指针变量没有被初始化.任何指针变量刚刚被创建的时候,是不会自动变成NULL指针的,他的缺省值是随机的,这个时候指针是乱指的.所以指针变量在被创建的同时应该被初始化,让指针指向合法的内存.     (2)指针P被free或者delect之后,没有设置为NULL:当指着被free之后,其实就是释放了指针指向这块内存进行释放,也就是说指针依旧是指向这个地址,但是这个地址上面的东西不要了,并且系统也不会将这块内

野指针 空指针 通用指针

空指针是一个特殊的指针值,也是唯一一个对任何指针类型都合法的指针值.指针变量具有空指针值,表示它当时处于闲置状态,没有指向有意义的东西.空指针用0表示,C语言保证这个值不会是任何对象的地址.给指针值赋零则使它不再指向任何有意义的东西.为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL.    程序里可以写 p = 0;     或者 p = NULL; 两种写法都把p置为空指针值.相对而言,前一种写法更容易使读程序的人意识到这里是一个指针赋值.操作NULL也会导致不可预知的错误 我

关于空指针NULL、野指针、通用指针

http://www.cnblogs.com/losesea/archive/2012/11/16/2772590.html 首先说一下什么是指针,只要明白了指针的含义,你就明白null的含义了.假设 有语句 int a=10;那么编译器就在内存中开辟1个整型单元存放变量a,我们假设这个整型单元在内存中的地址是 0x1000:那么内存0x1000单元中存放了数据10,每次我们访问a的时候,实际上都是访问的0x1000单元中的10.现在定义:int *p:                 p=&a

c++中的悬浮指针和野指针 二级指针

(1) c++中的悬浮指针:声明了但没有被付值的指针,它指向内存中的任意一个空间.避免悬浮指针的一个方法是开始就付值为NULL (2)"野指针"不是NULL指针,是指向"垃圾"内存的指针.人们一般不会错用NULL指针,因为用if语句很容易判断.但是"野指针"是很危险的,if语句对它不起作用.野指针的成因主要有两种: 一.指针变量没有被初始化.任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气.所以,指针变量在创建的同

野指针错误实例

野指针,也就是指向不可用内存区域的指针.通常对这种指针进行操作的话,将会使程序发生不可预知的错误. 这是野指针的定义,但很多C语言新手对它还是很陌生,下面我就简单举一个例子来看一看.在VC6.0中输入以下代码: #include <malloc.h> #include <stdio.h> void main() { char *p; //...可能有别的很多操作 if(p != NULL) { free(p); } else { p = (char *)malloc(4); } /