Data Structures

Started by
30 comments, last by Programmer16 18 years, 10 months ago
There, that's better. Note that the need for a copy constructor made indispensable the addition of const_iterator, since the copy constructor takes a const list&, so we need const-correct iterators provided by begin()/end().

#include <iostream>template<class T>class list{private:	struct listnode	{		listnode* prev_;		listnode* next_;		T data_;		listnode() {}		listnode(const T& data)	: 			data_(data) {}		listnode(listnode* prev, listnode* next) : 			prev_(prev), next_(next) {}		listnode(listnode* prev, listnode* next, const T& data) : 			prev_(prev), next_(next), data_(data) {}	};private:	listnode* head_;public:	// Give iterators access to the private list::listnode class.	friend class const_iterator;	friend class iterator;	// iterator for const lists.	class const_iterator	{		friend class list;	protected:		const listnode* node_;	public:		const_iterator() : node_(0) {}	protected:		const_iterator(const listnode* node ) : node_(node) {}	// All these member functions will be hidden in iterator, not overriden.	public:		const T& operator*() const { return node_->data_; }		const T* operator->() const { return &(node_->data); }		const_iterator& operator++()		{			node_ = node_->next_;			return *this;		}		const_iterator& operator--()		{			node_ = node_->prev_;			return *this;		}		bool operator==(const const_iterator& rhs) const { return node_ == rhs.node_; }		bool operator!=(const const_iterator& rhs) const { return !(*this == rhs);    }	};	class iterator :	// note: public inheritance without virtual destructor because we only use the	//       default destructor and iterator does not declare any new data members.		public const_iterator	{		friend class list;	public:		iterator() : const_iterator(0) {}	protected:		iterator(listnode* node ) : const_iterator(node) {}	// All these member functions will be hidden in iterator, not overriden.	public:		T& operator*() const { return const_cast<T&>(node_->data_); }		T* operator->() const { return const_cast<T*>(&(node_->data)); }		iterator& operator++()		{			node_ = node_->next_;			return *this;		}		iterator& operator--()		{			node_ = node_->prev_;			return *this;		}	};public:	list() 	{		// Can't put in init list, since we need the pointer to init the pointee.		// It is also advised (Sutter?) not to put new in init lists (leak on exception)		head_ = new listnode;		head_->prev_ = head_;		head_->next_ = head_;	}	list(const list& rhs) 	{		// Can't put in init list, since we need the pointer to init the pointee.		// It is also advised (Sutter?) not to put new in init lists (leak on exception)		head_ = new listnode;		head_->prev_ = head_;		head_->next_ = head_;		const_iterator first = rhs.begin();		const_iterator last = rhs.end();		for(;first != last; ++first)			push_back(*first);	}	list(const_iterator first, const_iterator last) 	{		// Can't put in init list, since we need the pointer to init the pointee.		// It is also advised (Sutter?) not to put new in init lists (leak on exception)		head_ = new listnode;		head_->prev_ = head_;		head_->next_ = head_;		for(;first != last; ++first)			push_back(*first);	}	~list()	{		while( !empty() ) 			erase( begin() );		delete head_;	}	list& operator=(const list& rhs)	{		assign(rhs.begin(), rhs.end());		return *this;	}public:	void swap(list& rhs)	{		std::swap(head_, rhs.head_);	}	void assign(const_iterator first, const_iterator last)	{		list tmp(first, last);		swap(tmp);	}public:	iterator begin()    { return iterator(head_->next_); }	iterator end()      { return iterator(head_);      }	const_iterator begin() const { return const_iterator(head_->next_); }	const_iterator end()   const { return const_iterator(head_);      }	bool empty() const 	{ return head_->next_ == head_; }public:	// Insert a new element before the specified location	iterator insert(const iterator& itor, const T& data)	{		listnode* node = const_cast<listnode*>(itor.node_);		listnode* newnode = new listnode(node->prev_, node, data);		node->prev_->next_ = newnode;		node->prev_ = newnode;		return iterator(newnode);	}	// Remove an element at the specified location	iterator erase(const iterator& itor)	{		listnode* node = const_cast<listnode*>(itor.node_);		listnode* nextnode = node->next_;		node->next_->prev_ = node->prev_;		node->prev_->next_ = node->next_;		delete node;		return iterator(nextnode);	}public:	void push_back(const T& data) {	insert(end(), data); }	void pop_back()               {	erase(--end());      }	void push_front(const T& data) { insert(begin(), data); }	void pop_front()               { erase(begin());        }};template<class T>void swap(list<T>& lhs, list<T>& rhs){	lhs.swap(rhs);}int main(){	list<int> l, ll;	std::cout << (l.empty() ? "list empty" : "list not empty") << std::endl;	l.push_back(2);	l.push_front(1);	std::cout << (l.empty() ? "list empty" : "list not empty") << std::endl;	list<int>::iterator it;	for( it = l.begin(); it != l.end(); ++it )	{		std::cout << *it << std::endl;	}	l.push_front(0);	l.push_back(3);	std::cout << (l.empty() ? "list empty" : "list not empty") << std::endl;	for( it = l.begin(); it != l.end(); ++it )	{		std::cout << *it << std::endl;	}	l.pop_back();	l.pop_back();	std::cout << (l.empty() ? "list empty" : "list not empty") << std::endl;	for( it = l.begin(); it != l.end(); ++it )	{		std::cout << *it << std::endl;	}	ll = l;	l.pop_front();	l.pop_front();	std::cout << (l.empty() ? "list empty" : "list not empty") << std::endl;	for( it = ll.begin(); it != ll.end(); ++it )	{		std::cout << *it << std::endl;	}	ll.pop_front();	ll.pop_front();	std::cout << (ll.empty() ? "list empty" : "list not empty") << std::endl;}


MrEvil - Yeah, I should have provided the postfix operators. Didn't bother since it's something trivial to add which wasn't indispensable to the class structure.

I prefer implementing them as follow rather than duplicate the code:

iterator operator++(int){   iterator old = *this;   operator++();   return old;}
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Advertisement
Thanks for all of the replies guys. Yeah, I didn't notice that I was creating 2 nodes, but I fixed that. I also changed my code so that it sets the next node correctly. I'm going to rewrite my code to see if I can figure it out. I've never actually programmed it right before (I always made the data a pointer, but thats not right. Thats why I am doing this.)

Yes I know I said that I'm doing ot for code base, but I'm still doing it for learning purposes (everything I do is for learning purposes.)

Edit:
Thanks Fruny, I had forgotten about the compiler names problem. I'll switch it. The operator was only used on temporary nodes, so It was modifying the list (well, it probably was since its a pointer to the node.)

Thanks again for everybodies help.
Okay, I'm still getting the error. I've fixed almost everything that I can see is wrong. I also did a comparision between my code, Fruny's code, and another class that I downloaded and they all seem to be similiar. Here is my test code:
class Texture{public:	std::string m_FileName;		Texture()	{		CDebugger::Get()->Output("main.cpp", __LINE__, "In Texture()");	}		~Texture()	{		CDebugger::Get()->Output("main.cpp", __LINE__, "In ~Texture()");	}		bool Create(const char* pFileName)	{		CDebugger::Get()->Output("main.cpp", __LINE__, "Loading texture : %s", pFileName);		m_FileName = pFileName;		return true;	}};TLinkedList<Texture>	m_Textures;void DrawTexture(const Texture* pTexture){	CDebugger::Get()->Output("main.cpp", __LINE__, "Drawing %s", pTexture->m_FileName.c_str());}int main(){	Texture pTexture;	pTexture.Create("cursor.bmp");	m_Textures.Add(pTexture);		pTexture.Create("char.bmp");	m_Textures.Add(pTexture);		pTexture.Create("tileset.bmp");	m_Textures.Add(pTexture);		for(unsigned int nIndex = 0; nIndex < m_Textures.Size(); ++nIndex)	{		Texture* pTexture = m_Textures.At(nIndex);		DrawTexture(pTexture);	}	return 0;}


Its still giving me the error on the delete pTemp line in the linked list's destructor.

Edit: All of the text is output correctly. And I double checked, I'm not trying to delete a null pointer.
Deleting a null pointer is not an error. Make sure you don't have a double-delete. Sorry, I'm not up to wading through your code today. Try diagramming your pointer manipulations. Good luck.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Deleting a null pointer is not an error. Make sure you don't have a double-delete. Sorry, I'm not up to wading through your code today. Try diagramming your pointer manipulations. Good luck.


It fails on the very first delete.
Odd behaivor, perhaps you should post the access violation, namely the address where the violation occours, and the address that it tries to access. Also, you should be doing this in debug mode.
I am doing this in debug mode (I do everything in debug until I release it.)

The addresses are:
m_pHead = 0x00322840pNode = 0x00322840pNode->m_pNext = 0x00322b68pTemp = 0x00322840

delete pTemp gives me an access violation on the first iteration of the loop.

A message box pops up and says:
"Unhandled exception in DFGames Code Base.exe: 0xC0000005: Access Violation."
It then tries to open LOCALE0.CPP. When I tell it cancel (since I don't know where to find it), it opens the disassembly window, pointing to this line:
0040C689 mov ecx,dword ptr[edx+eax*4]


If you need any other information, just say so.
Looks like an access violation due to printing out in your destructor. could be because your debugger singleton gets destructed first. or that the output window is gone, but I don't think it is because of the delete, but rather due to the destructor in your test Texture object... perhaps you should declare one of those as global, and see if you get the same error.
Quote:Original post by Arelius
Looks like an access violation due to printing out in your destructor. could be because your debugger singleton gets destructed first. or that the output window is gone, but I don't think it is because of the delete, but rather due to the destructor in your test Texture object... perhaps you should declare one of those as global, and see if you get the same error.


The printing is all done correctly (as I have already said). And its printing to a file, not a window. Its giving me an error on the line 'delete pTemp;'. And, I have already declared one globally.

Edit: I did it again, just to double check and no access violations.

Edit: Open mouth, insert foot. I figured I'd try commenting out the call to the debugger, just in case, its working now. I don't know why I'm getting an error though, at first I wasn't releasing it (on accident), so it should've still been active. But even when I was, I didn't realise it was doing it before the calls (since I forgot to add 'm_pDebugger = 0;' after I released it.)

Ok, I don't understand this. My debugger is still showing that it is created (I'm temporarily not releasing it) and now I'm getting a different error ("The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.)

Oh, and since my Get() function creates the debugger if it isn't already, it should work fine. Am I missing something here?

[Edited by - Programmer16 on June 20, 2005 2:14:08 PM]
seriously now try commenting out

CDebugger::Get()->Output("main.cpp", __LINE__, "In ~Texture()");

in the destructor for Texture.

because if I remember correctlly LOCALE0.CPP is part of the STD, and referenced in iostream

This topic is closed to new replies.

Advertisement