代码改变世界

VC5.0中的ATL的一个有趣的bug

2011-06-13 07:10  menggucaoyuan  阅读(421)  评论(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),是个包装后的数组,效果应该比上面的好多。