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.
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;
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
ASKER
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'.