Link to home
Start Free TrialLog in
Avatar of tantormedia
tantormediaFlag for United States of America

asked on

C++ vector deallocation problem

Dear experts,

In my MFC application, I have a CParagraph class which has the following member:
std::vector<CTake> m_vectorWaveTakes;

CTake is a structure that has no pointers, just integers and booleans.
When CParagraph is destroyed, I am getting an error:

Book.exe has triggered a breakpoint
The thread 'Win32 Thread' (0x12e8) has exited with code 0 (0x0).
HEAP[Book.exe]: Invalid Address specified to RtlValidateHeap( 01170000, 016428F8 )
Windows has triggered a breakpoint in Book.exe.
This may be due to a corruption of the heap, which indicates a bug in Book.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while Book.exe has focus.
The output window may have more diagnostic information.

The call stack shows that the problem happened in the vector destructor, but I don't know what could cause it.
Thanks.
Avatar of jkr
jkr
Flag of Germany image

Could you post the code for 'CTake'?
Avatar of tantormedia

ASKER

Here it is:
struct CTake
{
	CTime recordTime;
	DWORD waveLoc;
	int waveSize;
	int totalRaw;
	DWORD thisWaveTakeLoc;
	DWORD nextWaveTakeLoc;
	short waveCenter;
	short waveAveLevel;
	short waveMinNoise;
	BOOL  waveClipped;

	CTake() : recordTime(CTime::GetCurrentTime()), waveLoc(0), waveSize(0), totalRaw(0), thisWaveTakeLoc(0), nextWaveTakeLoc(0), waveCenter(0), waveAveLevel(0), waveMinNoise(0), waveClipped(FALSE){}
	CTake(CTime& time, DWORD loc, int size, int totRaw, short center, short level, short noise, BOOL clipped) : recordTime(time), waveLoc(loc), waveSize(size), totalRaw(totRaw), thisWaveTakeLoc(0), nextWaveTakeLoc(0), waveCenter(center), waveAveLevel(level), waveMinNoise(noise), waveClipped(clipped){}
};

Open in new window

Are you performing any 'erase()' operations on the vector? Also, does this also occur if you provide a copy constructor plus an assignment operator for 'CTake'?
No, I am not using erase().
I added the copy constructor and an assignment operator, and still got an error

Error      7      error C2558: struct 'CTake' : no copy constructor available or copy constructor is declared 'explicit'      c:\Program Files\Microsoft Visual Studio 9.0\VC\include\vector      1211

struct CTake
{
	CTime recordTime;
	DWORD waveLoc;
	int waveSize;
	int totalRaw;
	DWORD thisWaveTakeLoc;
	DWORD nextWaveTakeLoc;
	short waveCenter;
	short waveAveLevel;
	short waveMinNoise;
	BOOL  waveClipped;

	CTake() : recordTime(CTime::GetCurrentTime()), waveLoc(0), waveSize(0), totalRaw(0), thisWaveTakeLoc(0), nextWaveTakeLoc(0), waveCenter(0), waveAveLevel(0), waveMinNoise(0), waveClipped(FALSE){}
	CTake(CTime& time, DWORD loc, int size, int totRaw, short center, short level, short noise, BOOL clipped) : recordTime(time), waveLoc(loc), waveSize(size), totalRaw(totRaw), thisWaveTakeLoc(0), nextWaveTakeLoc(0), waveCenter(center), waveAveLevel(level), waveMinNoise(noise), waveClipped(clipped){}
	CTake(CTake& orig) : recordTime(orig.recordTime), waveLoc(orig.waveLoc), waveSize(orig.waveSize), totalRaw(orig.totalRaw), thisWaveTakeLoc(orig.thisWaveTakeLoc), nextWaveTakeLoc(orig.nextWaveTakeLoc), waveCenter(orig.waveCenter), waveAveLevel(orig.waveAveLevel), waveMinNoise(orig.waveMinNoise), waveClipped(orig.waveClipped){}
	CTake& operator=(const CTake& rhs)
	{
		recordTime = rhs.recordTime; waveLoc = rhs.waveLoc; waveSize = rhs.waveSize; totalRaw = rhs.totalRaw; thisWaveTakeLoc = rhs.thisWaveTakeLoc; nextWaveTakeLoc = rhs.nextWaveTakeLoc; waveCenter = rhs.waveCenter; waveAveLevel = rhs.waveAveLevel; waveMinNoise = rhs.waveMinNoise; waveClipped = rhs.waveClipped;
		return *this;
	}
};

Open in new window

Try to make that
CTake(const CTake& orig) : recordTime(orig.recordTime), waveLoc(orig.waveLoc), waveSize(orig.waveSize), totalRaw(orig.totalRaw), thisWaveTakeLoc(orig.thisWaveTakeLoc), nextWaveTakeLoc(orig.nextWaveTakeLoc), waveCenter(orig.waveCenter), waveAveLevel(orig.waveAveLevel), waveMinNoise(orig.waveMinNoise), waveClipped(orig.waveClipped){}

Open in new window

I could compile it, but unfortunately the problem is still there.
Does this project consist just of an executable or are other modules involved (e.g. DLLs)? If so, are you adding elements in one module and destroying the vector in another?
No, everything is done in one module.
Here is the call stack in the debug mode:

       Book.exe!_CrtIsValidHeapPointer(const void * pUserData=0x0162d8a8)  Line 2103      C++
      Book.exe!_free_dbg_nolock(void * pUserData=0x0162d8a8, int nBlockUse=1)  Line 1317 + 0x9 bytes      C++
       Book.exe!_free_dbg(void * pUserData=0x0162d8a8, int nBlockUse=1)  Line 1258 + 0xd bytes      C++
       Book.exe!operator delete(void * p=0x0162d8a8)  Line 373 + 0xb bytes      C++
       Book.exe!std::allocator<CTake>::deallocate(CTake * _Ptr=0x0162d8a8, unsigned int __formal=3)  Line 140 + 0x9 bytes      C++
       Book.exe!std::vector<CTake,std::allocator<CTake> >::_Tidy()  Line 1134      C++
       Book.exe!std::vector<CTake,std::allocator<CTake> >::~vector<CTake,std::allocator<CTake> >()  Line 560      C++
       Book.exe!CParagraph::~CParagraph()  Line 231 + 0xfd bytes      C++


So _CrtIsValidHeapPointer() returns false, thus:
_ASSERTE(_CrtIsValidHeapPointer(pUserData)); fails.
those error may occur because of any other stack issue.. if for example you ever returned a pointer to a local variable and use that pointer in the calling function you may overwrite pointer values of the internal vector member. i also have seen such errors when a pointer to a local arrray was passed for use in a callback or in a asynchronously running thread. whenever the function where the local array
was defined runs out of scope the memory could be reused by any other function and such way destroy valid contents.

i would check all my functions which returm a pointer or which pass a pointer to other functions if that passing was valid. or use a purifier to check your code.

Sara



Thank you for your answer. What is purifier?
Hm, what does the destructor of 'CParagraph' look like?
     ~CParagraph() { DeleteIf(m_pText); delete m_pImg; delete [] m_pNote; };
This is better:
      ~CParagraph() { delete [] m_pText; m_pText = NULL; delete m_pImg; delete [] m_pNote; m_pNote = NULL; };
Could you add the declaration as well? Also, how are the pointers allocated?
This is the declaration. It is an inline function.
The data is read from file:
            int TextLen;
            cf->Read(&TextLen,sizeof(TextLen));

            pPara->m_pText = new char[TextLen+1];
            cf->Read(pPara->m_pText,TextLen);
            pPara->m_pText[TextLen]='\0';

                  int NoteLen;
                  cf->Read(&NoteLen, sizeof(NoteLen));

                  if(NoteLen > 0)
                  {
                        pPara->m_pNote = new char[NoteLen + 1];
                        cf->Read(pPara->m_pNote, NoteLen);
                        pPara->m_pNote[NoteLen] = '\0';
                  }

                  if(pPara->m_pImg != NULL)
                  {
                        delete(pPara->m_pImg);
                  }
                  cf->Seek(pPara->m_WaveLoc,CFile::begin);
                  char tp[4];
                  cf->Read(tp,4);
                  if(!strncmp(tp,"PNG ",4))
                  {
                        ULONGLONG s;
                        cf->Read(&s,sizeof(s));

                        CFile fTemp("temp.png", CFile::modeCreate|CFile::modeReadWrite);
                        ULONGLONG sizeWritten = 0;
                        while(s > 0)
                        {
                              ULONGLONG partSize = min(s, 1000000);
                              BYTE* pData = new BYTE[partSize];
                              cf->Read(pData, partSize);
                              fTemp.Seek(sizeWritten, CFile::begin);
                              fTemp.Write(pData, partSize);
                              delete [] pData;
                              s -= partSize;
                              sizeWritten += partSize;
                        }
                        fTemp.Close();

                        pPara->m_pImg = new CImage();
                        HRESULT res = pPara->m_pImg->Load("temp.png");
                  }

Hm, OK, this is a little too complex apparently. When you debug the destructor of 'CParagraph' and examine 'm_vectorWaveTakes', does it still contain valid entries or does it seem to be overwritten with other data?
At least part of the data is invalid.
This part of code may be important:

                        CParagraph * pNewPara = new CParagraph();
                        memcpy(pNewPara,pPara,sizeof(CParagraph));
 ...
                        pNewPara->m_pText = NULL;
                        pNewPara->m_pImg = NULL;
                        pNewPara->m_pNote = NULL;
                        pNewPara->m_pMyChapter = NULL;
                        delete pNewPara;

Can deleting pNewPara corrupt a vector in pPara? It is *pPara that has the problem.
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I am getting an error:
error C2248: 'CObject::operator =' : cannot access private member declared in class 'CObject'
All members of CParagraph are public. What does it mean?
Is 'CParagraph' derived from 'CObject'?
Yes
Well, if it is necessary to keep that, you should give the assignment operator a try...
Thank you very much. Problem is solved.
a purifier is a tool that checks your code. it would for eXAMPLE find out if you made new but never delete a pointer or whether you write outside of array boundaries. a good tool even may have found out the error with the memcpy.

the CObject::operator=  is made private to prevent you to make copies from CObject derived classes without supplying an explicit operator= for your class. for the same reason also the copy constructor of CObject is private.

see MSDN:

The standard C++ default class assignment behavior is a member-by-member copy. The presence of this private assignment operator guarantees a compiler error message if you assign without the overridden operator. You must therefore provide an assignment operator in your derived class if you intend to assign objects of your derived class.

So when you don't have a copy constructor or operator= for your class, the compiler tries to use the baseclass functionality which fails because those functions are private to class CObject.

Sara
Thank you Sara.
From your phrase "a good tool even may have found out the error with the memcpy" I make a conclusion that a purifier is not a certain tool, but a general name for tools of such kind. Maybe you could give me a specific name or place where I can get it, or maybe an example how it is used?
Thanks again.
the most known probably is Purify from Rational. also LINT from Gimpel is widely used. unfortunately i don't know free tools but surely there are some.l

Sara
Thank you very much.