Link to home
Start Free TrialLog in
Avatar of xing2kin
xing2kin

asked on

memory error when CTypedPtrArray<CPtrArray, ClassType*> element class has any virtual method

In VC++ 6.0 console application with linking MFC dll on windows 2000 server, if the element class of CTypedPtrArray has any virtual method(s), memory access error will occur under the following case: allocate a object of ClassType from heap with "new", add the pointer to this object onto CTypedPtrArray object, take and remove an element from the head of CTypedPtrArray, do sth (e.g. access its data) with the element, finally memory access error occurs while deleting the element.

The example  always has such a  memory access error regardless of relase/debug modes as long as the element class has any virtual methods. If we remove specifier 'virutal' before name of class methods, everything works fine in both debug and release modes.


struct PacketHeader
{
	unsigned long tag;		
	unsigned short msgid;	
	unsigned short status;	
	unsigned long timestamp;	
	char callerid[12];		
};
 
class CTaskObj
{
public:
	SOCKADDR_IN		cliAddr;
	// struct sockaddr_in	cliAddr;
	struct PacketHeader	msgHeader;
	char* pDataBuf;
	int		nBufSize;
 
public:
	CTaskObj();
//	 ~CTaskObj();
	virtual ~CTaskObj();
};
 
CTaskObj::CTaskObj()
{
	memset(&cliAddr, 0, sizeof(cliAddr));
	memset(&msgHeader, 0, sizeof(msgHeader));
 
	pDataBuf = NULL;
	nBufSize = 0;
}
 
CTaskObj::~CTaskObj()
{
	if (pDataBuf)
	{
		delete[] pDataBuf; pDataBuf = NULL;
	}
}
 
bool AddTaskIntoQueue(CTypedPtrArray<CPtrArray, CTaskObj*>& arrTasks, USHORT nUdpPort, USHORT nMsgID)
{
	CTaskObj* pTask = NULL;					
	try
	{
		SOCKADDR_IN addrClient;
		memset(&addrClient, 0, sizeof(addrClient));
		addrClient.sin_port = htons(nUdpPort);
 
		char pRecvBuf[1024];
		memset(pRecvBuf, 0, sizeof(pRecvBuf));
		PacketHeader* pAppHeader = (PacketHeader *)pRecvBuf;
		pAppHeader->msgid = htons(nMsgID);
 
		pTask = new CTaskObj;
		if (!pTask)
		{
			throw(-1);
		}
		memset(pTask, 0, sizeof(CTaskObj));
 
		// - copy received packet into task object
		memmove(&(pTask->cliAddr), &addrClient, sizeof(addrClient));
		memmove(&(pTask->msgHeader), pAppHeader, sizeof(PacketHeader));
 
		// no extra (optional) data are used for this task.
		pTask->pDataBuf = NULL;
 
		// - add the task into the task queue of UdpOutThread
		arrTasks.Add(pTask);
		pTask = NULL;	
	}
	catch(...)
	{
		printf("-- failed to add task into job queue. \n");
	}
 
	if (pTask)
	{
		delete pTask; pTask = NULL;
	}
 
	return true;
}
 
 
void TestTaskObjQueue()
{
	CTypedPtrArray<CPtrArray, CTaskObj*> arrTasks;
	arrTasks.SetSize(0, 10);
 
	USHORT nUdpPort = 100;
	USHORT nMsgID = 1000;
 
	AddTaskIntoQueue(arrTasks, nUdpPort, nMsgID);
	printf("--- num of items in queue = %d \n", arrTasks.GetSize());
 
	CTaskObj* pTask = arrTasks.GetAt(0);
	arrTasks.RemoveAt(0);
 
	int port = ntohs(pTask->cliAddr.sin_port);
	int mid = ntohs(pTask->msgHeader.msgid);
	printf("---data: port=%u, msgid=%u ; num of items=%d \n", port, mid, arrTasks.GetSize());
 
	if (pTask)
	{
		delete pTask; pTask = NULL;
	}
}
 
int main(int argc, char** argv)
{
 TestTaskObjQueue();
return 0;
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Zoppo
Zoppo
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
Avatar of xing2kin
xing2kin

ASKER

Yeah, your solution helps fix the problem.  Thanks a lot!!!!

After commenting out the line [memset(pTask, 0, sizeof(CTaskObj))],  memory error because of [delete pTask] never occurs.

According to your comments on the above, conclusion might be drawn that one should NEVER intialize the dynamically-allocated  memory buffer of a class object with 'memset(-)' if the class has any virtual method(s) (i.e. it has VTABLE). Since  'memset(-)'  resets class' VTABLE, deleting the object buffer will
cause 'memory access error'.
You provided a good solution to this probblem. Furthermore, your comment helps understand why this problem occurs.
Hi xing2kin,

you're welcome - fine that I was able to help ...

And yes, I agree, one should never do this - in any case it's better to implement a suitable constructor, this help avoiding errors and minimizes the effort (because no one has to remember the object needs to be 'zero-ed' after creation). Further one can never say if maybe in future some members will be added which shouldn't ever be set to zero since they are initialized to something differen in the constructor.

Have a nice day,

best regards,

ZOPPO