全文仅个人分析,思考,若有错误,麻烦指正,令我学习,谢谢。
首先描述背景,了解代码前后经过:(无法还原真实场景及数据 -- 只还原问题
1 // nIndex = 0;Point有成员int x,int y;
2 // 当前m_aryPoint有两组数据[Point(20,1),Point(100,1)];
3 // 表达意思即:把Point(20,1)插入到Point(100,1)后,形成[Point(100,1),Point(20,1)];
4 m_aryPoint.InsertAt(nIndex+2, m_aryPoint[nIndex]); 
5 m_aryPoint.RemoveAt(nIndex);
代码简单明了,理论上不应该出现问题,但是在实际测试中发现,Debug运行出错,而Release运行正常;
Debug运行后的两组数据:(100,1)(-572662307,-572662307)
Release运行后的两组数据:(100,1)(20,1)
1、定位:断点调试,找到具体出错点,
发现由InsertAt函数引起,该函数于atlmfc\include\afxtempl.h文件中,是模板函数;
template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount /*=1*/)
 1 template<class TYPE, class ARG_TYPE>
 2 void CArray<TYPE, ARG_TYPE>::InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount /*=1*/)
 3 {
 4     ASSERT_VALID(this);
 5     ASSERT(nIndex >= 0);    // will expand to meet need
 6     ASSERT(nCount > 0);     // zero or negative size not allowed
 7 
 8     if(nIndex < 0 || nCount <= 0)
 9         AfxThrowInvalidArgException();
10     
11     if (nIndex >= m_nSize)
12     {
13         // adding after the end of the array
14         SetSize(nIndex + nCount, -1);   // grow so nIndex is valid
15     }
16     else
17     {
18         // inserting in the middle of the array
19         INT_PTR nOldSize = m_nSize;
20         SetSize(m_nSize + nCount, -1);  // grow it to new size
21         // destroy intial data before copying over it
22         for( int i = 0; i < nCount; i++ )
23             (m_pData + nOldSize + i)->~TYPE();
24         // shift old data up to fill gap
25         ::ATL::Checked::memmove_s(m_pData + nIndex + nCount, (nOldSize-nIndex) * sizeof(TYPE),
26             m_pData + nIndex, (nOldSize-nIndex) * sizeof(TYPE));
27 
28         // re-init slots we copied from
29         memset((void*)(m_pData + nIndex), 0, (size_t)nCount * sizeof(TYPE));
30         for( int i = 0; i < nCount; i++ )
31 #pragma push_macro("new")
32 #undef new
33             ::new( (void*)( m_pData + nIndex + i ) ) TYPE;
34 #pragma pop_macro("new")
35     }
36 
37     // insert new value in the gap
38     ASSERT(nIndex + nCount <= m_nSize);
39     while (nCount--)
40         m_pData[nIndex++] = newElement;
41 }
View Code
-------经查,当程序进入到下面的SetSize函数前,newElement,仍未正确数据,当SetSize函数运行后,newElement数据错误,变为 -572662307 = 0xDDDDDDDD,
-------经查,其他地方均不会导致newElement变化,由此确定,下面的代码将引起newElement的数据错乱;
1 if (nIndex >= m_nSize)
2 {
3     // adding after the end of the array
4     SetSize(nIndex + nCount, -1);   // grow so nIndex is valid
5 }

2、ARG_TYPE  newElement 的一生:它究竟是怎么回事,又是在哪里发生变化
为此,找到下面若干信息:
模板参数按值传递c++11-17 模板核心知识(七)—— 模板参数 按值传递 vs 按引用传递
强制省略拷贝或传递未实质化的对象(Mandatory Copy Elision or Passing Unmaterialized Objects)
----分析:如果按照上面文章所说,m_aryPoint[nIndex]也是[]运算符的重载,相当于函数的返回值,即产生一个临时变量,然后由这个临时变量作为参数,供给InsertAt函数使用,此时没有调用复制构造函数[★重★点★]
----解决方案:由上面得出结论,使用Point point = m_aryPoint[nIndex]; 作为中转,然后再调用m_aryPoint.InsertAt(nIndex+2, point); 发现已经成功解决问题;两个模式下均可成功运行;
----PS:该解决方案实际上在遇到问题时,便已经直接提出使用,并非思考良久才想到,只是为了逻辑,一步一步推导得到这个方案而如此描述。实际上很容易想到中转一下,解决问题(但无法明白问题为何出现)
----结论复制构造函数确实会影响此处,但是具体什么机制,仍不知道;(问题二)
3、SetSize函数:既然是你导致变化,那必定和你有关
为此,找到下面资料:
CArray::SetSize 的用法:       从未深入了解这些;
----猜想:既然文章中提到,可能会另开辟空间存储,那会不会是因为InsertAt引起的SetSize重新开辟空间导致newElement发生变化????
----实践(解决方案):为此提前给m_aryPoint设置10个数据预留空间(实际使用中只有这两组数据)(    m_aryPoint.SetSize(10);     ),这表明再调用InsertAt函数时,不需要重新开辟空间调整大小,而是可以直接把数据放至指定位置。----经过测试,该方法也能保障程序正常运行!
----总结:若提前开辟足够大小空间,InsertAt中不必再次开辟内存,newElement将始终保持正确值,不会出现数据错乱。但是一般而言无法提前确定长度,不太适用。此外SetSize究竟是何处导致了newElement数据的变化?仍然无从所知(问题三)
4、初步总结:分析前面两种方案的相似之处,找到核心问题
问题二中,提到的是临时变量、复制构造函数问题,归根结底,直接利用函数得到临时变量作为参数,开辟空间时,数据出错;而利用变量做参数可以正常使用;(传参中,变量产生的临时变量调用复制构造函数copy constructor
问题三中,提到的是开辟空间致使newElement数据变化;
综合一下这两个问题:因为m_aryPoint重新开辟空间,切换内存地址,而导致作为临时变量传过来的参数newElement发生数据错乱。2点提出的方案相当于给newElement切换一个存储地址,使其不受m_aryPoint变动影响;3点提出的方案相当于不让m_aryPoint发生变动,故而newElement不会变动;   ----由此这里必然存在一个关键点,即SetSize会影响到作为临时变量传过来的newElement,我无法处理这里的猜想(没有证实,所以只有解决方案,没有原理剖析)
5、相关资料查找:decay、Debug与Release
一文解决Debug与Release不一致问题:模板参数传递的区别(针对于函数模板的自动类型推导)

 

2022-09-22

posted on 2022-09-22 17:37  夜_归_人  阅读(143)  评论(0编辑  收藏  举报