CArray添加元素异常引发的思考
2015-01-29 21:24
471 查看
最近在使用MFC CArray的过程出现了一个比较诡异的问题,Add元素的时候居然出现了异常。
具体应用环境:VS2005 MFC XP SP3
在开始时使用了如下所示的类:
具体使用情况如下图所示:
当执行两次pyline.m_points.Add(pyline.m_points[0]);后发现了严重的异常问题,执行后按照想法结果应该如下图所示:(然而实际情况却并非如此,第一次add的时候是正常的,第二次add的时候发现最后一项变为了随机码)
以下为具体的调试步骤:
调试进入Add函数内部,具体代码如下图所示:
在此处的时候,发现newElement值为正常的(10,1)即pyline.m_points[0]正常。看来不是在这里出了问题。继续进入SetAtGrow源码,如下所示:
这里也没啥好看的,直接追踪进入SetSize源码,最终找到如下图所示关键位置代码:
//在下面这段代码未执行前的pyline.m_points值的内存布局如下图所示:
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData;
//执行上面这句代码之后,pyline.m_points值的内部布局如下图所示:
内存空间被释放了,看来是此处导致问题的产生,但是具体是什么原因导致了这种情况的出现呢?
总结:
关于CArray的工作原理简单说下,CArray开始申请一段内存用于存放Add进来的元素,当进来的元素超过CArray的当前的容量的时候,重新申请一块更大的新内存,然后将数据进行复制,然后将现有内存进行清理掉。
关于此问题的产生,要从CArray的声明说起,m_points的声明如下所示:
而在CArray的Add实现代码如下所示:
不知道你发现其中具体含义没有,可将上面的代码用Point,Point&用替换具体如下所示:
从上图的代码中我们发现:
CArray的两个模板参数,第一个参数就是CArray类数组元素的变量类型,后一个是函数调用时的参数类型。
当我们在Add元素进CArray时,使用的添加元素的引用(即指针)。但是在执行过程中我们调用了delete操作,这个时候CArray原有值可能被释放掉了,新元素的引用指向的就是如上图释放后的eeef ee ef等随机码。
推荐做法将要添加的新元素放在一个临时变量中,这样就不会问题了。不过很神奇的是,这样的代码在WinCe平台上居然运行至今都没出现过问题,只能说是好运爆棚啦。
具体应用环境:VS2005 MFC XP SP3
在开始时使用了如下所示的类:
Class CPolyLine { Public: CArray<Point, Point&> m_points; }
具体使用情况如下图所示:
CPolyLine pyline; //此处通过添加多个m_points,然后判断是否有环路,如果有环路则执行下面的操作。 //pyline.m_points.Add(); pyline.m_points.Add(pyline.m_points[0]);
当执行两次pyline.m_points.Add(pyline.m_points[0]);后发现了严重的异常问题,执行后按照想法结果应该如下图所示:(然而实际情况却并非如此,第一次add的时候是正常的,第二次add的时候发现最后一项变为了随机码)
以下为具体的调试步骤:
调试进入Add函数内部,具体代码如下图所示:
template<class TYPE, class ARG_TYPE> AFX_INLINE INT_PTR CArray<TYPE,ARG_TYPE>::Add(ARG_TYPE newElement) {INT_PTR nIndex= m_nSize; SetAtGrow(nIndex,newElement); return nIndex; }
在此处的时候,发现newElement值为正常的(10,1)即pyline.m_points[0]正常。看来不是在这里出了问题。继续进入SetAtGrow源码,如下所示:
template<class TYPE, class ARG_TYPE> void CArray<TYPE, ARG_TYPE>::SetAtGrow(INT_PTRnIndex, ARG_TYPEnewElement) { ASSERT_VALID(this); ASSERT(nIndex>= 0); if(nIndex < 0) AfxThrowInvalidArgException(); if (nIndex >= m_nSize) SetSize(nIndex+1,-1); m_pData[nIndex]= newElement; }
这里也没啥好看的,直接追踪进入SetSize源码,最终找到如下图所示关键位置代码:
#ifdef SIZE_T_MAX ASSERT(nNewMax<= SIZE_T_MAX/sizeof(TYPE)); // no overflow #endif TYPE* pNewData= (TYPE*) newBYTE[(size_t)nNewMax * sizeof(TYPE)];//CArray的容量不够了,这个是重新分配地址,明显这里应该不可能导致乱码的问题。 // copy new data from old 将原来CArray里面的内容全部复制到新位置 ::ATL::Checked::memcpy_s(pNewData,(size_t)nNewMax* sizeof(TYPE), m_pData, (size_t)m_nSize * sizeof(TYPE)); // construct remaining elements ASSERT(nNewSize> m_nSize); //将新添加元素的位置进行清零处理,这个显然不会有问题。 memset((void*)(pNewData + m_nSize),0, (size_t)(nNewSize-m_nSize) * sizeof(TYPE)); //不知道下面哥们到底在干啥,反正没影响我们的值。 for( int i = 0; i < nNewSize-m_nSize;i++ ) #pragma push_macro("new") #undef new ::new( (void*)( pNewData + m_nSize+ i ) ) TYPE; #pragma pop_macro("new")
//在下面这段代码未执行前的pyline.m_points值的内存布局如下图所示:
// get rid of old stuff (note: no destructors called)
delete[] (BYTE*)m_pData;
//执行上面这句代码之后,pyline.m_points值的内部布局如下图所示:
内存空间被释放了,看来是此处导致问题的产生,但是具体是什么原因导致了这种情况的出现呢?
总结:
关于CArray的工作原理简单说下,CArray开始申请一段内存用于存放Add进来的元素,当进来的元素超过CArray的当前的容量的时候,重新申请一块更大的新内存,然后将数据进行复制,然后将现有内存进行清理掉。
关于此问题的产生,要从CArray的声明说起,m_points的声明如下所示:
CArray<Point, Point&> m_points;
而在CArray的Add实现代码如下所示:
template<class TYPE, class ARG_TYPE> AFX_INLINE INT_PTR CArray<TYPE,ARG_TYPE>::Add(ARG_TYPE newElement) {INT_PTR nIndex= m_nSize; SetAtGrow(nIndex,newElement); return nIndex; }
不知道你发现其中具体含义没有,可将上面的代码用Point,Point&用替换具体如下所示:
AFX_INLINE INT_PTR CArray<Point,Point&>::Add(Point&newElement)
从上图的代码中我们发现:
CArray的两个模板参数,第一个参数就是CArray类数组元素的变量类型,后一个是函数调用时的参数类型。
当我们在Add元素进CArray时,使用的添加元素的引用(即指针)。但是在执行过程中我们调用了delete操作,这个时候CArray原有值可能被释放掉了,新元素的引用指向的就是如上图释放后的eeef ee ef等随机码。
推荐做法将要添加的新元素放在一个临时变量中,这样就不会问题了。不过很神奇的是,这样的代码在WinCe平台上居然运行至今都没出现过问题,只能说是好运爆棚啦。
相关文章推荐
- CArray 以 std::list 为元素的引发异常
- Node.js开发——添加mongoose模块引发的异常
- IE8下JQuery clone 出的select元素使用append添加option异常解决记录
- WF学习系列之六:工作流和宿主程序通讯异常引发的思考
- 解决遍历迭代器时添加元素会出现异常的问题
- mysql5.7group by异常引发的思考
- 关于 Arraylist和HashSet中元素比较的问题所引发的思考
- MVC系列——一个异常消息传递引发的思考
- HTML之表单元素“Password”引发的思考-你的密码安全吗???
- 一个异常引发的集合多线程思考
- QTreeWidget中添加widget 让后清除item 引发的异常
- mTips思维导图插件使用异常:给元素添加提示不现实
- 一个异常引发的对Hashtable和HashMap的思考
- 一个“java.lang.NoSuchMethodError”异常引发的思考
- (判断第三方输入法)在系统数字键盘上添加“完成”按钮引发的问题思考
- 通过切面捕捉方法异常,并添加错误码返回前端的一点思考
- 由自定义异常引发的对 __str__ 和 repr 的思考
- 新建oracle存储过程引发其他的存过程异常
- 关于馒头血案引发的法律相关的思考
- 委托:手工引发委托链中异常的例子