The Problem Of CArray(转)

44 篇文章 0 订阅

问题

CArray是一个很有用的C++类,他给我们编程带来诸多方便和高效。但是如果你比较仔细的话,会发现其实他有一个严重的错误,请看下面一段代码你可以清晰看到错误:

CArray< int,int&> my_carray;

 

int some_number = 1;

my_carray.Add(some_number);

 

for(int i=0; i<=10; i++) {

    my_carray.Add(my_carray[0]);

}

 

TRACE("\nIndex\tValue");

for(int j=0; j<=10; j++) {

    TRACE("\n%d\t%d", j, my_carray[j]);

}

The TRACE output is:

 

Index Value

0 1

1 -572662307

2 1

3 1

4 1

5 -572662307

6 1

7 1

8 1

9 -572662307

10 1

 

跟踪到Afxtempl.h

Afxtempl.h的一些小代码可以帮助你清晰的看出问题的所在,下面我们开始来看这个Add函数:

AFX_INLINE int CArray< TYPE, ARG_TYPE>::Add(ARG_TYPE newElement)

{

    int nIndex = m_nSize;

    SetAtGrow(nIndex, newElement);

    return nIndex;

}

其实并没有什么奇怪发生在这个Add函数中,他就是调用了SetAtGrow:

template< class TYPE, class ARG_TYPE>

void CArray< TYPE, ARG_TYPE>::SetAtGrow(int nIndex, ARG_TYPE newElement)

{

    ASSERT_VALID(this);

    ASSERT(nIndex >= 0);

 

    if (nIndex >= m_nSize)

        SetSize(nIndex+1, -1);

    m_pData[nIndex] = newElement;

}

注意这个SetSize 被调用时的条件表达式。下面我们再来看看这个SetSize函数: (这个函数比较大,只要仔细看加粗字体的代码就行了)

template< class TYPE, class ARG_TYPE>

void CArray< TYPE, ARG_TYPE>::SetSize(int nNewSize, int nGrowBy)

{

    ASSERT_VALID(this);

    ASSERT(nNewSize >= 0);

 

    if (nGrowBy != -1)

        m_nGrowBy = nGrowBy;  // set new size

 

    if (nNewSize == 0)

    {

        // shrink to nothing

        if (m_pData != NULL)

        {

            DestructElements< TYPE>(m_pData, m_nSize);

            delete[] (BYTE*)m_pData;

            m_pData = NULL;

        }

        m_nSize = m_nMaxSize = 0;

    }

    else if (m_pData == NULL)

    {

        // create one with exact size

#ifdef SIZE_T_MAX

        ASSERT(nNewSize <= SIZE_T_MAX/sizeof(TYPE));    // no overflow

#endif

        m_pData = (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];

        ConstructElements< TYPE>(m_pData, nNewSize);

        m_nSize = m_nMaxSize = nNewSize;

    }

    else if (nNewSize <= m_nMaxSize)

    {

        // it fits

        if (nNewSize > m_nSize)

        {

            // initialize the new elements

            ConstructElements< TYPE>(&m_pData[m_nSize], nNewSize-m_nSize);

        }

        else if (m_nSize > nNewSize)

        {

            // destroy the old elements

            DestructElements< TYPE>(&m_pData[nNewSize], m_nSize-nNewSize);

        }

        m_nSize = nNewSize;

    }

    else

    {

        // otherwise, grow array

        int nGrowBy = m_nGrowBy;

        if (nGrowBy == 0)

        {

          // heuristically determine growth when nGrowBy == 0

          //  (this avoids heap fragmentation in many situations)

          nGrowBy = m_nSize / 8;

          nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);

        }

        int nNewMax;

        if (nNewSize < m_nMaxSize + nGrowBy)

            nNewMax = m_nMaxSize + nGrowBy;  // granularity

        else

            nNewMax = nNewSize;  // no slush

 

        ASSERT(nNewMax >= m_nMaxSize);  // no wrap around

#ifdef SIZE_T_MAX

        ASSERT(nNewMax <= SIZE_T_MAX/sizeof(TYPE)); // no overflow

#endif

        TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];

 

        // copy new data from old

        memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));

 

        // construct remaining elements

        ASSERT(nNewSize > m_nSize);

        ConstructElements< TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);

 

        // get rid of old stuff (note: no destructors called)

        delete[] (BYTE*)m_pData;

        m_pData = pNewData;

        m_nSize = nNewSize;

        m_nMaxSize = nNewMax;

    }

}

我们可以看到这里发生了什么,在函数SetSize里m_pData被删除并接指向一块新的内存空间,而且一旦他返回SetAtGrow函数以后,执行代码m_pData[nIndex] = newElement 时,newElement引用的对象其实已经被释放了,里面的值已经不是原先的内容!

 

 

可能出现这种问题的情形

这个问题一般出现在同时满足以下三种情况时:

1)  CArray模板类的第二个参数是引用时;

2)  当你调用CArray的函数而且把一个已经存在的CArray元素作为调用参数时;

3)  调用Add,SetAtGrow,InsertAt引起的内存分配操作在函数SetSize里;

 

解决方法

我们还是有好多方法来避免和消除这个问题的:

1)      不要使用第二个参数为引用类型的CArrays对象。这个方法对于小数据类型的比较好用,比如int,但是对于大数据结构的类型就适合了。(举个例子CArray< int,int&> 会发生这个问题,而CArray< int,int> 这样就ok了)

2)      建立一个临时对象作为Add的参数对象。

3)      修改Afxtempl.h,在删除没m_pData之前完成赋值。

4)     std::vector

转自:http://huangdingjun.blog.163.com/blog/static/3110639200852351330579/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值