发现一个关于CString类的问题,它在Rrelease下释放不掉,因为我在程序里用到了CStringArray。而后我就做了个最简单的SDI应用程序用来测试CString类。在菜单中添加Test项来进行测试,在MytestView.h中定义如下两个数组类:typedef CArray<CString*,CString*> strArray;
typedef CArray<char*,char*> charArray;
定义两个类成员:strArray m_strArray;
                charArray m_charArray;
响应函数如下:
CMytestView::OnTest()
{
     int i = 0;
     for(i = 0;i < 1000000;i++)
     {
         CString* pstr = new CString;
         pstr->operator +=("hello world");
         m_strArray.Add(pstr);
     }
     for(i = 0;i < 1000000;i++)
     {
         delete m_strArray[i];
     }
     m_strArray.RemoveAll();
}
在Release版本下运行程序,响应菜单消息,通过任务管理器观察内存情况,发现内存有明显上升,可是没有下降,就停留在一个水平,之后不关闭主程序,再次响应菜单,发现内存又有明显上升,而后就降下来,降到第一次停留的水平,多试几次还是这样,就是说就第一次的内存没有释放掉。
   而在Debug下运行程序却一切正常,内存上升后就下降。当用CStringArray的时候,通过Add加元素,最后RemoveAll,也是同样的情况。
   之后我对函数做了如下修改:
CMytestView::OnTest()
{
     int i = 0;
     for(i = 0;i < 1000000;i++)
     {
         char* pchar = new char[i%10+1];
         m_charArray.Add(pchar);
     }
     for(i = 0;i < 1000000;i++)
     {
         delete m_charArray[i];
     }
     m_charArray.RemoveAll();
}
在Release和Debug下同样运行程序,响应消息,观察内存都是先上去后下来,一切正常,我就郁闷了。
难道CString类在Release和Debug下就有这么大不同,是CString类有问题,VC++6.0有问题还是我的用法有问题,望各位大虾赐教啊!

解决方案 »

  1.   

    没有人回答我的疑问吗?今天有建了个简单的MDI应用程序,在MytestView.h中定义成员数组:
    CString m_strArray[1000000];
    在菜单中加入“创建”菜单项。响应消息如下:
    CMytestView::OnConstruct()
    {
       int i = 0;
       for(i = 0;i < 1000000;i++)
       {
           m_strArray[i] = "abcdefghijklmn";
       }
    }
    运行程序,观察任务管理器,在Debug下,当响应菜单后,内存明显上升,关掉子窗口,不退出程序,内存立刻降下去,一切正常;可当在Release下运行程序,然后也关闭子窗口,发现内存上去后就没下来。到底是怎么回事啊,是VC++6.0在对CString类的在两中情况下有什么不同啊,会产生这样的奇怪现象,那到你们没有吗?有可以试一下。
      

  2.   

    你的第一段代码中:
        CString* pstr = new CString;不应该这么用吧。
    第三段代码中:
        CString m_strArray[1000000];用的好像也不对。
    CString本来就是个字符串指针,它一般不是在堆上分配内存的,如果你这样
    使用的话它将在堆上分配内存,占有很大的Memery。
      

  3.   

    是CString类有问题。而且很奇怪,在VC5下,把CString的相关源代码copy出来,做成一个新类CString1,然后把你的代码改为CString1的,内存就能释放了……(VC5下CString的源代码的DEBUG和RELEASE版没有太大区别)
      

  4.   

    to pomelowu(羽战士)兄。
    你的意思是说自己重新建立一个和CString一样的类:CString1;然后在VC5中用吗?VC5中没有CString类吗?
      

  5.   

    to  xift_2008 :
    我的用法有什么不对吗?那具体应该怎样用呢。还有占用内存是比较大,可为什么在Debug下能释放,而在Release下就不能释放内,你可以试一下吗?
      

  6.   

    CString本来就是个字符串指针,它一般不是在堆上分配内存的
    __________这个判断是错误的,CString是一个类,它有一个字符串指针成员m_pchData,其内存也是在堆上分配的。==========VC5中没有CString类吗?
    __________有,不然我也不会有“VC5下CString的源代码的DEBUG和RELEASE版没有太大区别”这个结论。实际上昨天晚上看到你的帖子,在VC6下面试过,发现问题,但是没找到原因。今天试了在VC5下还是有这个问题,而VC5的CString版本比VC6简单,准备从这里开刀。可是没想到重写了一个类,居然就正常了。
      

  7.   

    我只选了你的代码中会用到的3个函数:构造、析构、operator +=,然后就是这三个函数要用到的其他函数和宏:
    class C1String;struct C1StringData
    {
    long nRefs;     // reference count
    int nDataLength;
    int nAllocLength;
    // TCHAR data[nAllocLength] TCHAR* data()
    { return (TCHAR*)(this+1); }
    };
    extern AFX_DATA TCHAR fxChNil;
    const C1String& AFXAPI fxGetEmptyString();
    #define fxEmptyString fxGetEmptyString()class C1String
    {
    public:
    // Constructors
    C1String();
    ~C1String();
    const C1String& operator+=(LPCTSTR lpsz);

    // inlines
    C1StringData* GetData() const
    { ASSERT(m_pchData != NULL); return ((C1StringData*)m_pchData)-1; }

    operator LPCTSTR() const           // as a C string
    { return m_pchData; }

    protected:
    LPTSTR m_pchData;   // pointer to ref counted string data

    void Init()
    { m_pchData = fxEmptyString.m_pchData; }

    void AllocBuffer(int nLen);
    void ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
    void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);

    static void PASCAL Release(C1StringData* pData);
    static int PASCAL SafeStrlen(LPCTSTR lpsz)
    { return (lpsz == NULL) ? 0 : lstrlen(lpsz); }
    };实现代码是直接copy的源代码,唯一的不同仅在于为了不冲突,把类名和宏名替换了。
      

  8.   

    我来说说我的看法:
    首先:你声明的数组是typedef CArray<char*,char*> charArray;
    参数类型是char*
    所以对于你第一个OnTest()中的
    CString* pstr = new CString;
             pstr->operator +=("hello world");
             m_strArray.Add(pstr);在准备开始Add操作并在参数pstr传递前,系统会pstr隐式转化为char*指针,从而你保存的是CString对象内部缓冲区的char*指针而不是对象本身的指针,因此你在最后作delete m_charArray[i];操作时,并没有调用CString对象的析构函数,而直接去删除这片区域,但是你最后没有调用
    CString对象的ReleaseBuffer()函数(直接通过指针修改内部数据时最后要调用这个函数),
    所以最后会发生内存泄漏 
    对于第二个OnTest()
    你每次new char[i%10+1];操作时,都在堆空间分配一块连续的内存单元,单元类型是基本数据char,而不是CString,所以用m_charArray.Add(pchar);就是在数组中保存了这片区域首指针,
    最后delete m_charArray[i];时,就是基本的C++销毁char*型数据块的语法了,而不牵涉到MFC对象
    的销毁,自然不会出错
      

  9.   

    to pomelowu(羽战士) :
    谢谢你的回复,按照你的做法,那它的实现部分也是照旧copy过来的吗?但我还要用到一些其他功能啊,比如比较,去处左右空白,转换类型等,这样不是很麻烦吗?
    另外,我在想,既然在Debug下面没有问题,在Release下才有问题,那是不是在这两种情况下不同的编译机制会对CString类产生影响呢,有什么办法解决啊。?
      

  10.   

    而且释放CString成员m_pchData的成员不是ReleaseBuffer,而是Release
      

  11.   

    找到问题的原因才好解决,我所做的事情只是想去探寻原因而已。只是把源码copy出来,结果居然正常了,这挺让我郁闷的……
    下一步只有派生一个CString的子类来看看了。
      

  12.   

    派生我试过了,从CString类直接Public派生一个子类,没加其他,也是这样。
      

  13.   

    另外,我在想,既然在Debug下面没有问题,在Release下才有问题,那是不是在这两种情况下不同的编译机制会对CString类产生影响呢,有什么办法解决啊。?
      

  14.   

    那是不是在这两种情况下不同的编译机制会对CString类产生影响呢,有什么办法解决啊
    ______________我也是考虑到这个问题才拿VC5下的CString来下手的。因为这个版本的源码仅有一个地方用到了_DEBUG标示,是用来在Debug版下afxDump用的。也就是说有可能是编译优化方式的不同造成的这个问题。
    但是我重写了CString这个类就没有问题了也让我一下子找不到头绪了……
      

  15.   

    嗯.....50分我也要MCF CString 的内存分配是这样子的
    在DEBUG中直接使用 delete new在release 中:
    凡是字符长度<=512的,分别调用 
    FixedAlloced[ 64, 128,256,512 ]CFixedAlloc 类
    有两个链表 分别管理Alloc与Free块在Alloc方法中:
    如果Free链表中没有数据就 new 
    有的话就从Free链表中弹出一块使用
    并将该块保存在Alloc链表中在Free方法中:
    没有真正释放内存,只是将这块内存从Alloc链表中移到Free链表
    释放内存只有在它的析构中被调用.因为CFixedlAlloc是作为全局静态对象使用的( 有 64,128,256,512共4个)
    所以分配的内存就要等到程序退出才能释放.我们可以计算一下new 10000次的10 char字串的CString 对象所占内存
    1:它调用了FixedAlloc64.
      单个CString数据是 ( 64+1+ ( 12=(sizeof(CStringData)))) = 77个字节
      在CFixedAlloc中因维护单向链表再开销一个4字节的指针 = 81.
    2: new 10000次
       81*10000 = 810000 = 810K.
    3: delete .
       内存没有被释放.
    如果是 new 10000 次 10字长 char的对象.就是 11*100000 = 110K.
    delete 后.内存被释放.这就是MFC为什么比SDK多占内存的原因. PS:根据本人研究MFC代码认为MFC的CString类是印度程序员写的.
       理由如下: 以TrimRight函数为例.
       有效率的算法是从字符尾部查找到头部或不同字符结束.
       而且CString 保存了字串长度节约了strlen的开销.
       这样尢其在大字串中很有效.   但是CString 各个版本都是从字符头开始查找.
       想想TrimLeft与TrimRight都从Left开始,真是有点左右不分.   这个世界占左右不分的,记忆中是印度人才会这样. 
      

  16.   

    印度人的左手和右手……想想就寒……对了楼上,我也想过你说的这个,你看的是VC6的source,但是我说了今天把VC5下的代码copy出来研究了一下:
    CString::~CString()
    //  free any attached data
    {
    if (GetData() != afxDataNil)
    {
    if (InterlockedDecrement(&GetData()->nRefs) <= 0)
    delete[] (BYTE*)GetData();
    }
    }
    与VC6下不同,VC5版本的CString无论是Debug版还是Release版都只有这一个析构,中间也没有对字符串长度的判断,直接delete。但是仍然存在楼主的问题。
      

  17.   

    释放内存只有在它的析构中被调用.因为CFixedlAlloc是作为全局静态对象使用的( 有 64,128,256,512共4个)
    所以分配的内存就要等到程序退出才能释放.
    ______________________________________________________________________
    之后我建的MDI程序,CString数组作为子视图的成员,在关闭子窗口也不会被释放吗,也要等到整个程序退出才会是放?
    我怎么不到关于Release下调用FixedAlloced的说明啊,能否给些具体说明。
      

  18.   

    VC5的代码我没有.
    1:你的测试环境单不单纯,因为MFC其它类也许会有类似是操作.
    2:delete new 不能简单看成 malloc, free的,因为C++是可以重载操作符的.如果大家有兴趣研究技术,我把我写的类库放到网上,不知大家有没有兴趣
      

  19.   

    SRC\FIXALLOC.CPPCFixedAlloc::CFixedAlloc(UINT nAllocSize, UINT nBlockSize)
    {
    ASSERT(nAllocSize >= sizeof(CNode));
    ASSERT(nBlockSize > 1); m_nAllocSize = nAllocSize;
    m_nBlockSize = nBlockSize;
    m_pNodeFree = NULL;
    m_pBlocks = NULL;
    InitializeCriticalSection(&m_protect);
    }CFixedAlloc::~CFixedAlloc()
    {
    FreeAll();
    DeleteCriticalSection(&m_protect);
    }void CFixedAlloc::FreeAll()
    {
    EnterCriticalSection(&m_protect);
    m_pBlocks->FreeDataChain();
    m_pBlocks = NULL;
    m_pNodeFree = NULL;
    LeaveCriticalSection(&m_protect);
    }void* CFixedAlloc::Alloc()
    {
    EnterCriticalSection(&m_protect);
    if (m_pNodeFree == NULL) 
    {
    CPlex* pNewBlock = NULL;
    TRY
    {
    // add another block
    pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, m_nAllocSize);
    }
    CATCH_ALL(e)
    {
    LeaveCriticalSection(&m_protect);
    THROW_LAST();
    }
    END_CATCH_ALL // chain them into free list
    CNode* pNode = (CNode*)pNewBlock->data();
    // free in reverse order to make it easier to debug
    (BYTE*&)pNode += (m_nAllocSize * m_nBlockSize) - m_nAllocSize;
    for (int i = m_nBlockSize-1; i >= 0; i--, (BYTE*&)pNode -= m_nAllocSize)
    {
    pNode->pNext = m_pNodeFree;
    m_pNodeFree = pNode;
    }
    }
    ASSERT(m_pNodeFree != NULL);  // we must have something // remove the first available node from the free list
    void* pNode = m_pNodeFree;
    m_pNodeFree = m_pNodeFree->pNext; LeaveCriticalSection(&m_protect);
    return pNode;
    }void CFixedAlloc::Free(void* p)
    {
    if (p != NULL)
    {
    EnterCriticalSection(&m_protect); // simply return the node to the free list
    CNode* pNode = (CNode*)p;
    pNode->pNext = m_pNodeFree;
    m_pNodeFree = pNode;
    LeaveCriticalSection(&m_protect);
    }
    }
      

  20.   

    SRC\FIXALLOC.H#ifndef __FIXALLOC_H__
    #define __FIXALLOC_H__#include "afxplex_.h"/////////////////////////////////////////////////////////////////////////////
    // CFixedAllocclass CFixedAlloc
    {
    // Constructors
    public:
    CFixedAlloc(UINT nAllocSize, UINT nBlockSize = 64);// Attributes
    UINT GetAllocSize() { return m_nAllocSize; }// Operations
    public:
    void* Alloc();  // return a chunk of memory of nAllocSize
    void Free(void* p); // free chunk of memory returned from Alloc
    void FreeAll(); // free everything allocated from this allocator// Implementation
    public:
    ~CFixedAlloc();protected:
    struct CNode
    {
    CNode* pNext;   // only valid when in free list
    }; UINT m_nAllocSize;  // size of each block from Alloc
    UINT m_nBlockSize;  // number of blocks to get at a time
    CPlex* m_pBlocks;   // linked list of blocks (is nBlocks*nAllocSize)
    CNode* m_pNodeFree; // first free node (NULL if no free nodes)
    CRITICAL_SECTION m_protect;
    };#ifndef _DEBUG// DECLARE_FIXED_ALLOC -- used in class definition
    #define DECLARE_FIXED_ALLOC(class_name) \
    public: \
    void* operator new(size_t size) \
    { \
    ASSERT(size == s_alloc.GetAllocSize()); \
    UNUSED(size); \
    return s_alloc.Alloc(); \
    } \
    void* operator new(size_t, void* p) \
    { return p; } \
    void operator delete(void* p) { s_alloc.Free(p); } \
    void* operator new(size_t size, LPCSTR, int) \
    { \
    ASSERT(size == s_alloc.GetAllocSize()); \
    UNUSED(size); \
    return s_alloc.Alloc(); \
    } \
    protected: \
    static CFixedAlloc s_alloc \// IMPLEMENT_FIXED_ALLOC -- used in class implementation file
    #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \
    CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \#else //!_DEBUG#define DECLARE_FIXED_ALLOC(class_name)     // nothing in debug
    #define IMPLEMENT_FIXED_ALLOC(class_name, block_size)   // nothing in debug#endif //!_DEBUG#endif
      

  21.   

    CMytestView::OnTest()
    {
         int i = 0;
         for(i = 0;i < 1000000;i++)
         {
             CString* pstr = new CString;
             //pstr->operator +=("hello world"); 如果是空的CString没有问题
             m_strArray.Add(pstr);
         }
         for(i = 0;i < 1000000;i++)
         {
             delete m_strArray[i];
         }
         m_strArray.RemoveAll();
    }全部释放
    无论你用什么CString的函数让CString不为空就会有你说的Release问题
    而这样又不会有问题CMytestView::OnTest()
    {
         int i = 0;
         for(i = 0;i < 1000000;i++)
         {
             CString* pstr = new CString;
             pstr->operator +=("hello world"); 
             m_strArray.Add(pstr);
             pstr->Empty();
         }
         for(i = 0;i < 1000000;i++)
         {
             delete m_strArray[i];
         }
         m_strArray.RemoveAll();
    }全部释放很见鬼的问题
    CMytestView::OnTest()
    {
         int i = 0;
         for(i = 0;i < 1000000;i++)
         {
             CString* pstr = new CString;
             pstr->operator +=("hello world"); 
             m_strArray.Add(pstr);
             if (i > 500000)
                 m_strArray[i-500000]->Empty();
         }
         for(i = 0;i < 1000000;i++)
         {
             delete m_strArray[i];
         }
         m_strArray.RemoveAll();
    }内存还有一半没有施放 CMytestView::OnTest()
    {
         int i = 0;
         for(i = 0;i < 1000000;i++)
         {
             CString* pstr = new CString;
             pstr->operator +=("hello world"); 
             m_strArray.Add(pstr);
         }
         for(i = 0;i < 1000000;i++)
         {
             pstr->Empty();
             delete m_strArray[i];
         }
         m_strArray.RemoveAll();
    }不会释放建议不要保留大量的CString到数据中可以用CHAR*
    想要对字符操作的时候用临时的CString操作完成Copy回去
    E.g.
        CHAR* p = m_chaArray.GetAt(index);
        CString str(p);
        .......... //str.Find += Left....
        strcpy(p, (LPCTSTR)str);//保证p的长度
      

  22.   

    1:你的测试环境单不单纯,因为MFC其它类也许会有类似是操作
    ____________单纯的定义为何?一个MFC向导建立的项目能不能够上你说的单纯的条件?============2:delete new 不能简单看成 malloc, free的,因为C++是可以重载操作符的.
    ____________有点常识的人都不会这样看,而且正因为没有这样看,所以我才重写了一个C1String类,主要精力花在了析构上。另外,“因为C++是可以重载操作符的”在这里没什么意义。
      

  23.   

    最后一个
    pstr->Empty();应为m_strArray[i]->Empty();
      

  24.   

    有没有什么地方是说明CString类在Release下是通过CFixedAlloc申请内存的,而Debug不是呢。
      

  25.   

    SRC\STRCORE.CPP#ifndef _DEBUG#pragma warning(disable: 4074)
    #pragma init_seg(compiler)#define ROUND(x,y) (((x)+(y-1))&~(y-1))
    #define ROUND4(x) ROUND(x, 4)
    AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));#endif //!_DEBUGvoid CString::AllocBuffer(int nLen)
    // always allocate one extra character for '\0' termination
    // assumes [optimistically] that data length will equal allocation length
    {
    ASSERT(nLen >= 0);
    ASSERT(nLen <= INT_MAX-1);    // max size (enough room for 1 extra) if (nLen == 0)
    Init();
    else
    {
    CStringData* pData;
    #ifndef _DEBUG
    if (nLen <= 64)
    {
    pData = (CStringData*)_afxAlloc64.Alloc();
    pData->nAllocLength = 64;
    }
    else if (nLen <= 128)
    {
    pData = (CStringData*)_afxAlloc128.Alloc();
    pData->nAllocLength = 128;
    }
    else if (nLen <= 256)
    {
    pData = (CStringData*)_afxAlloc256.Alloc();
    pData->nAllocLength = 256;
    }
    else if (nLen <= 512)
    {
    pData = (CStringData*)_afxAlloc512.Alloc();
    pData->nAllocLength = 512;
    }
    else
    #endif
    {
    pData = (CStringData*)
    new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
    pData->nAllocLength = nLen;
    }
    pData->nRefs = 1;
    pData->data()[nLen] = '\0';
    pData->nDataLength = nLen;
    m_pchData = pData->data();
    }
    }void FASTCALL CString::FreeData(CStringData* pData)
    {
    #ifndef _DEBUG
    int nLen = pData->nAllocLength;
    if (nLen == 64)
    _afxAlloc64.Free(pData);
    else if (nLen == 128)
    _afxAlloc128.Free(pData);
    else if (nLen == 256)
    _afxAlloc256.Free(pData);
    else  if (nLen == 512)
    _afxAlloc512.Free(pData);
    else
    {
    ASSERT(nLen > 512);
    delete[] (BYTE*)pData;
    }
    #else
    delete[] (BYTE*)pData;
    #endif
    }CString::~CString()
    //  free any attached data
    {
    if (GetData() != _afxDataNil)
    {
    if (InterlockedDecrement(&GetData()->nRefs) <= 0)
    FreeData(GetData());
    }
    }
      

  26.   

    to roscoe(草上飞):
       谢谢!那我能不能在我不用CString的时候调用CFixedAlloc::FreeAll()来释放掉呢。
      

  27.   

    to roscoe(草上飞):
       谢谢,为什么我将STRCORE.CPP中的:
    #define ROUND(x,y) (((x)+(y-1))&~(y-1))
    #define ROUND4(x) ROUND(x, 4)
    AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
    拷贝过来,再把FIXALLOC.H和FIXALLOC.CPP有拷贝过来。
    然后#include "FIXALLOC.H";
    再在程序里手工调用:
    #ifndef _DEBUG
    _afxAlloc64.FreeAll();
    _afxAlloc128.FreeAll();
    _afxAlloc256.FreeAll();
    _afxAlloc512.FreeAll();
    #endif
    还是不能释放掉呢。
    应该要怎么用啊?
      

  28.   

    可以的,你把那些个_afxFixAllocXX重新定义一下.
    建议如下定义:
    *.h
    void DoFixedAlloc_FreeAll();//
    .CPP#ifndef _DEBUG#pragma warning(disable: 4074)
    #pragma init_seg(compiler)AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
    void  DoFixedAlloc_FreeAll()
    {
       _afxAlloc64.FreeAll();
       _afxAlloc64.FreeAll();
       ...
    }
    #else // if debug nop
    void  DoFixedAlloc_FreeAll()
    {
    }
    #endif //!_DEBUG因为 AFX_STATIC 是在 afxver_.h 定义成 extern 的
    所以你可以在外重定义没问题
      

  29.   

    我也试了真的不行.
    而且看 CPlex的代码,也根本不能调用FreeAll.它是FREE所有在用的模块.
    用STL或者自已写个String类吧.反正高手都是这样干的的:)
      

  30.   

    好象还是不行啊。
    我在MytestView.h中定义全局函数void DoFixedAlloc_FreeAll();
    在.cpp中include "FIXALLOC.H"
    实现全局函数:
    #ifndef _DEBUG#pragma warning(disable: 4074)
    #pragma init_seg(compiler)#define ROUND(x,y) (((x)+(y-1))&~(y-1))
    #define ROUND4(x) ROUND(x, 4)AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
    AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));
    void  DoFixedAlloc_FreeAll()
    {
       _afxAlloc64.FreeAll();
       _afxAlloc128.FreeAll();
       _afxAlloc256.FreeAll();
       _afxAlloc512.FreeAll();
    }
    #else // if debug nop
    void  DoFixedAlloc_FreeAll()
    {}
    #endif //!_DEBUG
    通过菜单响应消息:
    void CMytestView::OnDestroy() 
    {
    // TODO: Add your command handler code here
    for(int i = 0;i < 1000000;i++)
    {
    m_strArray[i].Empty();
    }
    DoFixedAlloc_FreeAll();
    }
    还是不行啊。怎么回事??
      

  31.   

    to roscoe(草上飞), Phourm() , pomelowu(羽战士) :
       万分感谢你们,我要对这个帖子追加给分,怎么追加啊。?
      

  32.   

    CString在Release版采用内存池内存块技术,可以防止频繁的new、delete,要知道new、delete实际都相当于代价不低的函数,所以采用这种方法是以增加内存消耗的方法来提升速度。不过CString类还不是十分的优化,我写过的一个字符串类就比它快一至两倍。
      

  33.   

    我测试过,没有任何问题啊!?// CStringTest.cpp : Defines the entry point for the console application.
    //#include "stdafx.h"
    #include "CStringTest.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif/////////////////////////////////////////////////////////////////////////////
    // The one and only application object
    #include <Afxtempl.h>CWinApp theApp;using namespace std;typedef CArray<CString*,CString*> strArray;
    typedef CArray<char*,char*> charArray;strArray m_strArray;
    charArray m_charArray;void OnTest()
    {
    for(int i = 0; i < 1000000; i++)
    {
    CString* pstr = new CString;
    pstr->operator +=("hello world");
    m_strArray.Add(pstr);
    }
    for(i = 0;i < 1000000;i++)
    {
    delete m_strArray[i];
    }
    m_strArray.RemoveAll();
    }
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
    int nRetCode = 0;

    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
    // TODO: change error code to suit your needs
    cerr << _T("Fatal Error: MFC initialization failed") << endl;
    nRetCode = 1;
    }
    else
    {
    // TODO: code your application's behavior here.
    OnTest();

    }

    return nRetCode;
    }
      

  34.   

    console程序,VC6的DEBUG和release下都测试过,程序开始和结束循环没有看出来有你们说的内存泄露啊。
      

  35.   

    To  ipgk(loboho),“CString在Release版采用内存池内存块技术”这句话能不能详细的说明一下?我大概也看过CString的源码,它用来防止频繁的new、delete的关键似乎是采用的引用计数。不知道你是不是这个意思?如果是的话,那么在Debug版下同样是采用的引用计数啊。而且Debug版和Release版采用完全不同的内存分配管理机制并不可取,因为真是这样的话,debug版存在的意义就有些不明确了。To 楼上,我在不同环境下测试都存在这个问题啊。To 楼主,
    就停留在一个水平,之后不关闭主程序,再次响应菜单,发现内存又有明显上升,而后就降下来,降到第一次停留的水平,多试几次还是这样,就是说就第一次的内存没有释放掉。
    __________________我觉得可能是编译器的原因,在release版本下让heap manager做“假释放”,以备以后的使用。第二次以后运行这段代码内存的增加量远比第一次运行的小。
    但是始终有点说不通。诚如前面提到的,VC6版本的CString在Release下可能没有真正的释放内存,那么为什么VC5下就是直接delete的,仍然有这个状况?而第二次运行这段代码又貌似正常。不过因为以后使用相同的代码不会造成额外的内存负担,姑且认为从整体上说没有内存泄露吧,应该不会有太大问题。不然就自己写个类吧。
      

  36.   

    找到问题所在了,是Microsoft Visual Studio的bug,在http://support.microsoft.com/default.aspx?kbid=193100#toc中有介绍,并且有Service Pack,不过我下了装上,还是不行,不知道怎么回事,大概要对原程序做些改动吧,不过用VC++.net试过了,没有问题,谢谢各位了。
      

  37.   

    你说得这个BUG还不是这个问题.
    我把VC6 CString内存管理的分析,在blog写了一篇.
    不过不写不知道,我的文笔真是差啊-_-!!
    我对产生问题的原因总结为是教条主义.
    固执于采用C++的new delete操作符操纵内存造成的.
    所以VC7中还是改成C的malloc realloc free方式了.地址在这,敬请斧正
    http://blog.csdn.net/roscoe/archive/2005/05/22/377949.aspx不怕被人笑话.我是坚持认为CString 是印度阿三写的.不然怎么会这么臭
      

  38.   

    我对产生问题的原因总结为是教条主义.
    固执于采用C++的new delete操作符操纵内存造成的.
    __________________________________
    VC6 CString的内存管理并不是一种缺憾,这只不过是一个取舍的问题。严格说CString的内存管理方式并不叫内存池,所谓内存池是指预先开辟一大块空间,程序需要使用时便从池中取出一定的空间,使用完毕即归还池中,可提高效率和防止系统级的内存碎片。CString的做法是用过的内存并不释放掉,而是回收起来供下一次使用,性质跟内存池是一样的,都是程序自身介入内存管理,一般来说程序自己管理内存均比系统管理高效。你对比下VC6的CString和std::string的执行效率就知了。但是有得必有失,在效率提升的同时也造成空间得不到及时释放,但不是泄露,它不会象内存泄露那样可能造成内存逐渐的耗尽,因为内存始终在自己控制之内。还有一个缺点是造成理解的困难,因为一般程序员不知道CString使用内存块回收管理的话,就认为一个CSting释放了它储存字符串的空间也必定释放了,这样程序的内部实现与程序员的预期不一样,这样就有可能产生问题(虽然一般不会)。所以如果VC7的CString放弃了了内存块回收管理,应该是出于稳定大于一切的考虑,取与舍而已,不过我觉得它最好有一个编译开关,由程序员决定用哪一种方式。
    的确用惯C++的new、delete很多人都忘了malloc realloc ,其实没有构造函数时new的调用就相当于malloc。至于realloc,它有可能接近于一次malloc的开销,也可能稍大于一次free加一次malloc的开销。
      

  39.   

    delete m_charArray[i];-------------------------
     这个大错! 应为:     delete [] m_charArray[i];
      

  40.   

    我想说明一下 楼主的这个问题,其实, 根本没有问题:   第一次调用OnTest,内存大量上长升:   这里有两个地方需要大量的内存:  10000个 CString 对象, 和长10000 CString* 数组.
      为什么第一次释放了所有的数据内存并没有下降. 其实这里下降会有的,不过响应太快,你没看出来.但是为什么会停在某一水平? 这是因为CArray 的内存管理引起的. 你可以看看 CArray 的源代码, RemoveAll 是不会 释放 已经申请的内存.
      其后,你再进行分配串操作, 这时CArray 已经不用再重新分配内存了.   楼主将数组定义为 函数的局部变量试试,我相信这种情况将不复存在. 内存会完全释放掉.=================
      

  41.   

    To: pomelowu(羽战士) ( ) 
    引用计数是用来处理引用安全问题的(共享方问,数据保护),new 与delete的动作是被它引发的.
    nRefs > 1 == 共享 :如果调用者要对数据修改,则拷贝一个副本到本地后再修改.
    nRefs == -1 上锁:读,写前都先拷贝一个副本.
    ==================================================================
    To: loveghb(温柔的毒药)
    你应该在new 之前与 之后与delete之后,中断一下可以看出区别.
    ==================================================================
    To: ipgk(loboho) ( ) 
    ==================================================================
    VC7中是改了, 从IAtlMemMgr派生 crt,local,global,com几个对象
    CStringData中加了一个IAtlMemMgr指针.
    不觉得更安全,但这样写代码结构要比VC6清楚些.
    应该是出于稳定大于一切的考虑,取与舍而已==>我不这么看,我们也得允许MS有犯错误权利不是^_^
    realloc开销接近于malloc+free的情况只会在realloc时连续空间不足才会出现.
    对于字符串处理中,大部分情况是realloc的开销要小于malloc的.PS: VC7 的 CString现在是内存还是只增加不减少,不过VC6是不会释放,现在它会释放了.哈哈
      

  42.   

    roscoe(草上飞) :那你说VC6 CString的错误究竟是什么?PS: VC7 的 CString现在是内存还是只增加不减少,不过VC6是不会释放,现在它会释放了.哈哈
    …………………………………………………………………………………………
    这句我不理解。
      

  43.   

    1: 对于VC6中, ervinsas(sas) 说得BUG是一个, 
    Release 下不大于512b的时CString析构后内存得不到回收,算不算.2:. VC7 中 share | Lock 时拷贝副本不谈,当正常情况下时 GetBuffer
    调用的是PrepareWrite2
    ATL_NOINLINE void PrepareWrite2( int nLength )
    {
        CStringData* pOldData = GetData();
        if( pOldData->nDataLength > nLength )
        {
            nLength = pOldData->nDataLength;
        }
        if( pOldData->IsShared() )
        {
            Fork( nLength );
        }
        else if( pOldData->nAllocLength < nLength ) // 只有大于原有内存才会调用Reallocate
        {
             int nNewLength = pOldData->nAllocLength;
             if( nNewLength > 1024 )
             {
       nNewLength += 1024;
             }
    else
    {
    nNewLength *= 2;
    }
    if( nNewLength < nLength )
    {
    nNewLength = nLength;
    }
    Reallocate( nNewLength );
        }
    }
    ===============================================================
    就是说正常情况下CString(7)引发Reallocate的条件是nRefs=1 And nLength > nAllocLength.
    这么一来是不是没有办法将内存缩小了?
    我这没装VC.net 你可以试下,先GetBuffer(1M),ReleaseBuffer,再GetBuffer(1K),ReleaseBuffer
    看看GetAllocLength()是什么变化.
    还过7中CStringDaTa::Release 是会调用Free的,所以析构后内存还是能正常回收的
      

  44.   

    那个Bug不是本贴讨论的问题,不过的确是CString的错误。Release 下不大于512b的时CString析构后内存得不到回收,算不算.
    ——————————————————————-
    这点我说了是取舍问题了,他并不是MFC程序员的失误,而是故意这样做的,就算不得错。其实如果我来写CString的话,我还是宁愿选VC6的内存块回收方式,因为这样的确大大提高了效率。另外要说内存用完就忙不失交还系统,表面是节约内存,实际上造成系统内存碎片的增多,系统的内存维护链表增大,访问内存就越慢,同时即使你的空余内存总和够用,碎片太多的话,也可能出现申请不到内存的情况。第2点我就不说了,我也没.net。不过想问roscoe(草上飞)的机子内存是多少,为什么这么紧张内存:)
      

  45.   

    呵呵,纯是习惯,倒不是紧张内存,再说了分析嘛,自然得多考虑点.
    对于string对象,内存碎片倒确是首要考虑的