VC5.0中的ATL的一个有趣的bug
2011-06-13 07:10 menggucaoyuan 阅读(424) 评论(2) 编辑 收藏 举报
从VC5.0开始,ATL正式作为VC的一部分开始发布,可能M$对它的成熟度已经很自信。在阅读这个版本的ATL的源码时,ATLWIN.cpp中有如下代码:
BOOL CDynamicChain::SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID /* = 0 */)
{
// first search for an existing entry
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
m_pChainEntry[i]->m_pObject = pObject;
m_pChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
// search for an empty one
for(i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] == NULL)
{
m_pChainEntry[i] = pEntry; //注意此处,pEntry还没有被赋值,就加入m_pChainEntry中,是一个明显的bug
return TRUE;
}
}
// add a new one
ATL_CHAIN_ENTRY** ppNew = NULL;
ATLTRY(ppNew = new ATL_CHAIN_ENTRY*[m_nEntries + 1]);
if(ppNew == NULL)
{
delete pEntry;
return FALSE;
}
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
if(m_pChainEntry != NULL)
{
memcpy(ppNew, m_pChainEntry, m_nEntries * sizeof(ATL_CHAIN_ENTRY*));
delete [] m_pChainEntry;
}
m_pChainEntry = ppNew;
m_pChainEntry[m_nEntries] = pEntry;
m_nEntries++;
return TRUE;
}
CDynamicChain::SetChainEntry具有初始化m_pChainEntry并向其中添加元素的功能,上面在数组 m_pChainEntry中添加值的时候,pEntry明显还没有被赋值,所以在此处执行return语句,肯定时不正确的,所以应该把"add a new one"代码段的为pEntry赋值的语句提前。
另外,在"add a new one"代码段中添加一个新的元素时,数组每次只扩冲一个元素大小,效率肯定很低。如果考虑空间和时间的折中,至少也应该扩充0.75大小的空间。
我修改后的代码如下:
BOOL CDynamicChain::SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID /* = 0 */)
{
// first search for an existing entry
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
m_pChainEntry[i]->m_pObject = pObject;
m_pChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
// search for an empty one
for(i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] == NULL)
{
m_pChainEntry[i] = pEntry; //注意此处,pEntry还没有被赋值,就加入m_pChainEntry中,是一个明显的bug
return TRUE;
}
}
// add a new one
ATL_CHAIN_ENTRY** ppNew = NULL;
int nNewSize = (int)((float)m_nEntries * 1.75f);
ATLTRY(ppNew = new ATL_CHAIN_ENTRY*[nNewSize]);
if(ppNew == NULL)
{
delete pEntry;
return FALSE;
}
memset((void*)ppNew, NULL, sizeof(ATL_CHAIN_ENTRY) * nNewSize);
if(m_pChainEntry != NULL)
{
memcpy(ppNew, m_pChainEntry, m_nEntries * sizeof(ATL_CHAIN_ENTRY*));
delete [] m_pChainEntry;
}
m_pChainEntry = ppNew;
m_pChainEntry[m_nEntries] = pEntry;
m_nEntries = nNewSize
return TRUE;
}
下面给出VC6.0的AfxWin.h中这个函数的代码:
BOOL SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID = 0)
{
// first search for an existing entry
for(int i = 0; i < m_aChainEntry.GetSize(); i++)
{
if(m_aChainEntry[i] != NULL && m_aChainEntry[i]->m_dwChainID == dwChainID)
{
m_aChainEntry[i]->m_pObject = pObject;
m_aChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
// search for an empty one
for(i = 0; i < m_aChainEntry.GetSize(); i++)
{
if(m_aChainEntry[i] == NULL)
{
m_aChainEntry[i] = pEntry;
return TRUE;
}
}
// add a new one
BOOL bRet = m_aChainEntry.Add(pEntry);
if(!bRet)
{
delete pEntry;
return FALSE;
}
return TRUE;
}
上面可以看出VC6.0中这个bug已经去除,而且数组改用m_aChainEntry(类型为CSimpleArray),是个包装后的数组,效果应该比上面的好多。
BOOL CDynamicChain::SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID /* = 0 */)
{
// first search for an existing entry
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
m_pChainEntry[i]->m_pObject = pObject;
m_pChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
// search for an empty one
for(i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] == NULL)
{
m_pChainEntry[i] = pEntry; //注意此处,pEntry还没有被赋值,就加入m_pChainEntry中,是一个明显的bug
return TRUE;
}
}
// add a new one
ATL_CHAIN_ENTRY** ppNew = NULL;
ATLTRY(ppNew = new ATL_CHAIN_ENTRY*[m_nEntries + 1]);
if(ppNew == NULL)
{
delete pEntry;
return FALSE;
}
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
if(m_pChainEntry != NULL)
{
memcpy(ppNew, m_pChainEntry, m_nEntries * sizeof(ATL_CHAIN_ENTRY*));
delete [] m_pChainEntry;
}
m_pChainEntry = ppNew;
m_pChainEntry[m_nEntries] = pEntry;
m_nEntries++;
return TRUE;
}
CDynamicChain::SetChainEntry具有初始化m_pChainEntry并向其中添加元素的功能,上面在数组 m_pChainEntry中添加值的时候,pEntry明显还没有被赋值,所以在此处执行return语句,肯定时不正确的,所以应该把"add a new one"代码段的为pEntry赋值的语句提前。
另外,在"add a new one"代码段中添加一个新的元素时,数组每次只扩冲一个元素大小,效率肯定很低。如果考虑空间和时间的折中,至少也应该扩充0.75大小的空间。
我修改后的代码如下:
BOOL CDynamicChain::SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID /* = 0 */)
{
// first search for an existing entry
for(int i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] != NULL && m_pChainEntry[i]->m_dwChainID == dwChainID)
{
m_pChainEntry[i]->m_pObject = pObject;
m_pChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
// search for an empty one
for(i = 0; i < m_nEntries; i++)
{
if(m_pChainEntry[i] == NULL)
{
m_pChainEntry[i] = pEntry; //注意此处,pEntry还没有被赋值,就加入m_pChainEntry中,是一个明显的bug
return TRUE;
}
}
// add a new one
ATL_CHAIN_ENTRY** ppNew = NULL;
int nNewSize = (int)((float)m_nEntries * 1.75f);
ATLTRY(ppNew = new ATL_CHAIN_ENTRY*[nNewSize]);
if(ppNew == NULL)
{
delete pEntry;
return FALSE;
}
memset((void*)ppNew, NULL, sizeof(ATL_CHAIN_ENTRY) * nNewSize);
if(m_pChainEntry != NULL)
{
memcpy(ppNew, m_pChainEntry, m_nEntries * sizeof(ATL_CHAIN_ENTRY*));
delete [] m_pChainEntry;
}
m_pChainEntry = ppNew;
m_pChainEntry[m_nEntries] = pEntry;
m_nEntries = nNewSize
return TRUE;
}
下面给出VC6.0的AfxWin.h中这个函数的代码:
BOOL SetChainEntry(DWORD dwChainID, CMessageMap* pObject, DWORD dwMsgMapID = 0)
{
// first search for an existing entry
for(int i = 0; i < m_aChainEntry.GetSize(); i++)
{
if(m_aChainEntry[i] != NULL && m_aChainEntry[i]->m_dwChainID == dwChainID)
{
m_aChainEntry[i]->m_pObject = pObject;
m_aChainEntry[i]->m_dwMsgMapID = dwMsgMapID;
return TRUE;
}
}
// create a new one
ATL_CHAIN_ENTRY* pEntry = NULL;
ATLTRY(pEntry = new ATL_CHAIN_ENTRY);
if(pEntry == NULL)
return FALSE;
pEntry->m_dwChainID = dwChainID;
pEntry->m_pObject = pObject;
pEntry->m_dwMsgMapID = dwMsgMapID;
// search for an empty one
for(i = 0; i < m_aChainEntry.GetSize(); i++)
{
if(m_aChainEntry[i] == NULL)
{
m_aChainEntry[i] = pEntry;
return TRUE;
}
}
// add a new one
BOOL bRet = m_aChainEntry.Add(pEntry);
if(!bRet)
{
delete pEntry;
return FALSE;
}
return TRUE;
}
上面可以看出VC6.0中这个bug已经去除,而且数组改用m_aChainEntry(类型为CSimpleArray),是个包装后的数组,效果应该比上面的好多。