Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

sirSolarius

Deconstructor not firing...

This topic is 5217 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m having a problem where an object''s deconstructor never fires, and that means that a member variable never gets deconstructed either. I''m not sure why this is happening. I have a base memory-managed object called MemObj, a smart pointer to it called MemPtr and then the derived object is an InputKeymap. When an object has nothing pointing to it, it adds itself to the MemoryManager''s list of objects to be deleted. In this example, the keymap _is_ deleted by the MemoryManager, but the destructor is never called for some reason (I set a breakpoint at InputKeymap::~InputKeymap() and it''s never fired). This leaves a reference to the escape Functor, and so that never deletes. Please help! This is the base memory-managed object:
#ifndef _ERIK_MEMOBJ_H_DEFINED
#define _ERIK_MEMOBJ_H_DEFINED

#include <assert.h>
#include <list>

#include "MemoryManager.h"		// include the memory management class


/////////////////////////////////////////////////////////////////////////////

//	MEMOBJ.H

/////////////////////////////////////////////////////////////////////////////

//

// AUTHOR: ERIK GOLDMAN

// DATE:  6-3-04

// DESC: The base memory-managed object class

// NOTES:

// TOFIX:

/////////////////////////////////////////////////////////////////////////////


class MemObj
{
protected:
	int m_refs;

public:
	MemObj()
	:m_refs(0)
	{
	}

	virtual ~MemObj()
	{
	}

	void attach()
	{
		++m_refs;
	}
	void release()
	{
		--m_refs;
		if(m_refs==0)		// nothing points to the object

		{
			MemoryManager::getInstance()->markForDeletion(this);
		}
	}
};


/////////////////////////////////////////////////////////////////////////////

//	MEMPTR

/////////////////////////////////////////////////////////////////////////////

//

// AUTHOR: ERIK GOLDMAN

// DATE:  6-3-04

// DESC: A smart pointer to a memory-managed object

// NOTES:

// TOFIX:

/////////////////////////////////////////////////////////////////////////////


template <class T>
class MemPtr
{
protected:
	T *obj;

public:
	MemPtr()
	:obj(0)
	{
	}

	MemPtr(const MemPtr<T> &p)
	:obj(p.obj)
	{
		if(obj)
			obj->attach();
	}

	MemPtr(T *p)
	:obj(p)
	{
		if(obj)
			obj->attach();
	}

	~MemPtr()
	{
		if(obj)
			obj->release();
		obj=0;
	}

	inline MemPtr &operator=(T *p)
	{
		if(p != obj)		// prevent against self-assignment

		{
			if(obj)
				obj->release();
			obj=p;
			if(obj)
				obj->attach();
		}
		return *this;
	}

	inline MemPtr &operator=(const MemPtr &p)
	{
		if(p.obj != obj)	// prevent against self-assignment

		{
			if(obj)
				obj->release();
			obj=p.obj;
			if(obj)
				obj->attach();
		}
		return *this;
	}

	inline T& operator*() const
	{
		assert(obj!=0);
		return *obj;
	}

	inline T *operator->() const
	{
		assert(obj!=0);
		return obj;
	}

	inline bool operator==(const MemPtr<T> &p)
	{
		return(p.obj == obj);
	}

	inline bool operator ==(const T *p)
	{
		return(obj == p);
	}

	inline bool operator !()
	{
		return (obj == 0);
	}

	inline bool isValid()
	{
		return (obj != 0);
	}
};

#endif
And then the class itself:
// InputKeymap.cpp: implementation of the InputKeymap class.

//

//////////////////////////////////////////////////////////////////////


#include "InputKeymap.h"

//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////


InputKeymap::InputKeymap()
{
}

InputKeymap::~InputKeymap()
{
	Erase();
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: Assign

// Desc: Maps a key to a function

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void InputKeymap::Assign(int key, MemPtr<Functor> &f)
{
	m_keymap[key]=f;
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: Unassign

// Desc: Unmaps a key completely

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void InputKeymap::Unassign(int key)
{
	m_keymap[key]=0;
	m_keymap.erase(key);
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: Erase

// Desc: Erases the keymap and deletes its functors

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void InputKeymap::Erase()
{
	std::map<int, MemPtr<Functor> >::iterator iter=m_keymap.begin(), e=m_keymap.end();
	while(iter != e)
	{
		Unassign(iter->first);				// set the functor to 0 for memory management

		++iter;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: Update

// Desc: Checks the input manager for pressed keys, and activates functions if pressed

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void InputKeymap::Update()
{
	InputManager *p=InputManager::getInstance();
	std::map<int, MemPtr<Functor> >::iterator iter=m_keymap.begin(), e=m_keymap.end();
	while(iter != e)
	{
		if(p->keyDown(iter->first))		// if key is pressed

			(*(iter->second))();			// activate the function

		++iter;							// then advance to the next key

	}
}
The memory manager:
// MemoryManager.cpp: implementation of the MemoryManager class.

//

//////////////////////////////////////////////////////////////////////


#include "MemoryManager.h"

MemoryManager *MemoryManager::instance;
//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////


MemoryManager::MemoryManager()
:m_deadObjects()
{
}

MemoryManager::~MemoryManager()
{
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: getInstance

// Desc: Gets an instance of the singleton

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

MemoryManager *MemoryManager::getInstance()
{
	if(!instance)
		instance=new MemoryManager();
	return instance;
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: CheckMemory

// Desc: Iterates through the list of dead objects and deletes them

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void MemoryManager::checkMemory()
{
	if(!m_deadObjects.empty())
	{
		m_iter=m_deadObjects.begin();
		while(m_iter != m_deadObjects.end())
		{
			delete *m_iter;
			m_tempIter=m_iter;
			++m_tempIter;
			m_deadObjects.erase(m_iter);
			m_iter=m_tempIter;
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: markForDeletion

// Desc: Adds the MemObj to the deletion queue when nothing points to it

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void MemoryManager::markForDeletion(MemObj *m)
{
	m_deadObjects.push_back(m);
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: Destroy

// Desc: Cleans up the memory created by, ironically, the memory manager

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void MemoryManager::Destroy()
{
	while(!m_deadObjects.empty())	// because deleted stuff frees other things

	{
		checkMemory();
	}
	delete instance;
	instance=0;
}
And finally, the main loop:
//////////////////////////////////////////////////////////////////////////////////////////

// Name: MainLoop

// Desc: The main loop of the engine... implemented for memory management reasons

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////

void MainLoop()
{
	MemoryManager *memManage=MemoryManager::getInstance();

	MemPtr<InputKeymap> keymap=new InputKeymap();
	MemPtr<Functor> escape=new Functor(&ExitProgram);
	keymap->Assign(DIK_ESCAPE, escape);

	g_state=RUNNING;
	
	InputManager *mainInput=InputManager::getInstance();

	MSG msg;		// Message for recieving Windows messages


	while(g_state == RUNNING)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		mainInput->Update();
		memManage->checkMemory();
		
		keymap->Update();

		if(g_state==PAUSED)
			g_state=RUNNING;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////

// Name: WinMain

// Desc: the entry function for the entire engine.

// Notes:

// ToFix:

//////////////////////////////////////////////////////////////////////////////////////////


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	//_CrtSetBreakAlloc(48);

	////////////////// Enable memory leak detection ///////////

	#ifdef _DEBUG
		_CrtDumpMemoryLeaks();
		_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	#endif
	//////////////////////////////////////////////////////////


	// Start by creating my class and window

	WindowManager *wnd=WindowManager::getInstance();
	wnd->CreateClass("Kutatas", WindowsMsgHandler, hInstance);
	wnd->SpawnWindow("Kutatas Engine", 1024, 768);
	wnd->SetAsGLWindow();
	wnd->setFullScreen(true);

	MainLoop();

	// destroy everything

	WindowManager::getInstance()->Destroy();
	LogManager::getInstance()->Destroy();
	InputManager::getInstance()->Destroy();

	MemoryManager::getInstance()->Destroy();
	return 1;	
}

Share this post


Link to post
Share on other sites
Advertisement
I didn''t have time to go through your code in detail, but I did breeze through it. My suggestion: in MemoryManager''s destructor shouldn''t you be calling checkMemory to ensure that all objects are deleted? Why not put this in to verify that the InputKeyMap''s destructor is called.

P.S. It''s good that you''re setting a breakpoint. I once made the naive assumption that I could log when a destructor is fired, but it was a statically allocated object that got destroyed after std::cout! Thus, my logs never made it to the console...


[ CodeDread ]

Share this post


Link to post
Share on other sites
quote:
Original post by Pipo DeClown
Your MemPtr class doesn''t have a virtual destructor..

--
You''re Welcome,
Rick Wong
- Google | Google for GameDev.net | GameDev.net''s DirectX FAQ. (not as cool as the Graphics and Theory FAQ)



But MemPtr isn''t inherited, MemObj is. The deconstructor for MemPtr calls when the MemPtr is destroyed, decrementing the reference counter. The problem is that when the references run out, the MemObj data from the heap never fires a deconstructor. And as for rypyr''s comment, I call MemoryManager::Destroy() anyway, which calls checkMemory() until there are no dead objects. Putting that in the destructor serves the same purpose, as you can only delete the singleton by calling Destroy().

Share this post


Link to post
Share on other sites
Hello sirSolarius,

problem could be in how your iterating through to dead objects.

what are you using vector or list?

In ethier one the earse returns a iterator to the next element following. Maybe the order iteration is getting changed by the actual erase.

Think about vector which is an array of elements.
You deleted one elememt then save off a copy of your iterator.
Next increment this copy to point to the next element in the array.
Then called earse on the vector passing in org. iterator.
If this happen at front of middle the vector needs to resize and shift all elements down one after the earse iterator.
Now your copy of the iterator is pointing to an element that has been shift down one.
You copy this back to your org. iterator but not sure what it points to now.
dose it point to the shifted down element or the one after it?

Agian this could be, but I am not sure. I have had problems with map and earsing iterators so be careful.

so maybe your loop should be.


void MemoryManager::checkMemory()
{
if(!m_deadObjects.empty())
{
m_iter=m_deadObjects.begin();
while(m_iter != m_deadObjects.end())
{
delete *m_iter;
m_iter=m_deadObjects.erase(m_iter);
}
}
}


Lord Bart

[edited by - lord bart on June 7, 2004 1:14:45 PM]

Share this post


Link to post
Share on other sites
Lord Bart,
I went through my debugger after your post and discovered something even more unusual: delete is called, but the destructor is not.

Does this have anything to do with the forward declaration that I have to make in the MemoryManager class?

Share this post


Link to post
Share on other sites
This probably isn''t the problem, but I noticed that your MemObj::release() function doesn''t check for cases where m_refs is negative. Ideally, this shouldn''t happen, but there are always those cases..

And for reference, it''s called the destructor, not the deconstructor.

Share this post


Link to post
Share on other sites
quote:
Original post by psykr
This probably isn''t the problem, but I noticed that your MemObj::release() function doesn''t check for cases where m_refs is negative. Ideally, this shouldn''t happen, but there are always those cases..

And for reference, it''s called the destructor, not the deconstructor.


Heh, sorry about that. Well, the problem is definitely not with the markForDeletion or even checkMemory code... the delete statement calls, but the destructor never calls. If the destructor called, then all the memory would clean up perfectly.

Share this post


Link to post
Share on other sites
Hello sirSolarius,

Put a break point in every desturctor used for you memory manager and all classes put in to a MemPtr.

Then run and check, I would say you are hitting a destructor but not the one you think you should hit.

Lord Bart

Share this post


Link to post
Share on other sites
Are you sure that you are not delete-ing a void pointer? No destructor is called in that case. Difficult to tell without the header for MemoryManager.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!