Thread: Converting a program. From Vector to List

  1. #1
    Registered User
    Join Date
    Aug 2007
    Location
    U.K.
    Posts
    148

    Converting a program. From Vector to List

    Hi,


    I have a program, which uses a vector to store some pointers.

    The program works fine.

    I'm trying to convert it to use a doubly linked list now though.

    At the top of the program I changed:

    Code:
    vector<Diary *>vectorname;
    to

    Code:
    #include <list>
    
    list<Diary *>vectorname;
    
    list<Diary*>::iterator i = vectorname.begin();
    Later on in the program I access an element of the (former) vector using:

    Code:
    if(vectorname[a]->getName() == leadToDelete)

    And use:

    Code:
    if(advance(i,a)->getName() == leadToDelete)
    {
          //do something
    }
    instead.

    BUt when I try it fails to compile, saying:

    error C2227: left of '->getName' must point to class/struct/union/generic type
    I was hoping this would be a simple way to do the conversion, but I have made a mistake in the code above it seems.

    Could anyone show me how I do this?

    Many thanks for any help!
    Last edited by Swerve; 12-03-2009 at 07:33 PM.

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    The advance function doesn't return anything. You need to call it first, then use the iterator i inside the if. Don't forget to dereference i to get a Diary* so you can use ->getName().

    I assume you're aware that doing random access with a list is often too expensive to justify using it over other containers. That's why there's no operator[] other random access member function for lists.

  3. #3
    Registered User
    Join Date
    Aug 2007
    Location
    U.K.
    Posts
    148
    OK, I decided to start the program from the beginning again.

    Using:

    Code:
    List<Base *>listname;
    I am trying to delete a specific node from the list. Here is the case inwhich it happens:

    Code:
    case 'b':
    
    	{
    		//---------------------
    		//Delete a 'Base'
    		//---------------------
    		cout << "You chose to delete" << endl;
    
    		if(listname.size() == 0)
    		{
    			cout << "There are no nodes currently stored" << endl;
    		}
    
    		else
    		{
    			string NodeToDelete;
    			cout << "Enter the datamember 'name' of the node to delete" << endl;
    			getline (cin, NodeToDelete);
    
    			for(unsigned int a = 0; a<listname.size(); ++a)
    			{
    
    
    					if(listname[a]->getName() == NodeToDelete)
    					     {
    					           cout <<"Found a match " << endl;
    
    					          listname.remove(NodeToDelete);
    
    					         cout << "Match deleted" << endl;
    					     }
    			}
    		}
    		break;
    	}
    I'm having trouble with the two lines in bold.

    The first worked in the vector, but I know you can't access list elements like that.

    The second line in bold is trying to remove the matching name using list::remove() but it say's:

    'leadtodelete' : undeclared identifier
    Last edited by Swerve; 12-03-2009 at 09:51 PM.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Code:
    List<Base *>listname;
    ...
    ...
    string NodeToDelete;
    listname.remove(NodeToDelete);
    These lines look incorrect. If listname is a list of Base pointers then remove must take in a Base pointer. You are telling it to remove a string yet you have not added strings to the list. If you meant to deref the iter and then grab the string from the object this is not exactly what you posted.

    This may be more of what you were looking for. There aren't all that many differences between how you use or interface with the STL containers but there are significant differences in what they do and how they store their data. Once you get used to these differences you will be able to switch from list to vector to map to any other container with little or no trouble.

    Code:
    typedef std::list<Base *> BaseList;
    typedef BaseList::iterator BaseListIter;
    BaseList listname;
    ...
    ...
    bool Foo::Remove(const std::string &NodeToDelete)
    {
       bool result = false;
       BaseListIter iter(listname.begin());
       BaseListIter end(listname.end());
       
       while (iter != end)
       {
          if ( (*iter)->getName() == NodeToDelete )
          {
                //Cleanup the memory allocated by the object
                delete *iter;
              
                //Remove the object pointer from the list
                listname.erase(iter);
                result = true;
                break;
           }
           ++iter;
        }
        return result;
    }
    list::remove() will remove a unique element matching the type you pass in. However in your case your list contains Base pointers and not strings. Because remove() has no way of knowing that the Base object has a function that returns a string identifier and therefore will not use it as the determining factor for removal you must do some footwork to get it to do what you want. The code above, if it is correct as is, should iterate the list from start to finish and will check the name of the object via getName() which I'm assuming is in the Base object. If the name matches the name that is passed into the function the memory pointed to in the list will be cleaned up and list::erase() will erase the element which will remove it from the list. list::remove() will look for a Base pointer matching the one you specify b/c that is what you put into the list. If your function removed from the list based on Base pointer addresses then your removal would work. Your removal is using a different criteria or custom criteria for removal and thus list::remove() cannot be used.

    'leadtodelete' : undeclared identifier
    That has to be a typo since I do not see 'leadtodelete' anywhere in the code.


    If you need random read/write access to the container list is probably not the best choice. Vector allows for random access and acts similar to an array. If you need to key off items by a unique key then std::map is a good choice. std::set also does this but you cannot alter the value at the key in std::set. Both set and map do not allow you to change the key.
    Last edited by VirtualAce; 12-03-2009 at 10:41 PM.

  5. #5
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by Swerve View Post
    I am trying to delete a specific node from the list. Here is the case inwhich it happens:
    Wanting to perform deletions from the middle of a list as well as accessing items by index is almost certainly a logical error.

    To me it indicates that a more appropriate container is a map. You can then access any item using a known key value, and potentially delete it, without affecting the ability to access any other item by its key value. The difference is that the key value is no longer its array index, but is instead some kind of cookie value..
    I'd say it's time to learn about more containers.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  6. #6
    Registered User
    Join Date
    Aug 2007
    Location
    U.K.
    Posts
    148
    Bubba, Daved and iMalc thanks soo much for your help!

    I've played and changed stuff here and there, and this is the result:

    I'm asking the user for the node to delete, based on a datamember's value , putting that into a string and them deleting matches from the std::list.

    Code:
    case 'b':
    
    	{
    	      //---------------------
    		//Delete a 'Lead(an object of a derived class)'
    		//---------------------
    		//---------------------
    		//NOTE: DELETES * MATCHES
    		//---------------------
    		cout << "You chose to delete an object of the derived class" << endl;
    
    		if(listname.size() == 0)
    		{
    			cout << "There are no derived class objects in the list" << endl;
    		}
    		else
    		{
    			string leadToDelete;
    			cout << "Enter the name of the lead to delete" << endl;
    			getline (cin, leadToDelete);
    
    			i iter(listname.begin());
    			i end(listname.end());
    
    			while (iter != end)
    			{
    				if ( (*iter)->getName() == leadToDelete )
    				{
    					delete *iter;
    					listname.erase(iter++);//Iterator incremented here because 'iter' becomes invalid after the erase(): 
    				}
    				else
    				{
    					++iter;
    				}
    			}
    		}
    		break;
    	}
    Do you think it looks OK?

    Now I'm trying to iterate through a list.

    I want the user to be able to display node's values, by letting the user go forwards and backwards.

    Code:
    case 'f':
    	{
    		i iter(listname.begin());
    		i beginning(listname.begin());
    		i end(listname.end());
    
    		string nextPreviousChoice = "";
    		do{
    
    			cout << "Press 'n' then Enter to show next, 'p' then Enter for previous and 'q' then enter to return to menu" << endl;
    			getline (cin, nextPreviousChoice);
    
    			//FORWARDS
    			if(nextPreviousChoice == "n")
    			{
    				if(iter == listname.end())
    				{
    					cout << "at end of list" << endl;
    				}
    				else
    				{
    					//++iter;//Can overun end of list and cause a crash!!!
    					cout << "Name: " << (*iter)->getName() << endl; 
    					//++iter;//seems not right either
    				}
    			}
    			//BACKWARDS
    			else if(nextPreviousChoice == "p")
    			{
    				if(iter == listname.begin())
    				{
    					cout << "at beginning of list" << endl;
    				}
    				else
    				{
    					--iter;
    					cout << "Name: " << (*iter)->getName() << endl; 
    				}
    			}
    			else
    			{
    				cout << "Tou made a invalid choice" << endl;
    			}
    		}
    		while(nextPreviousChoice != "q");
    
    		break;//return to main menu
    	}

    When a user over runs the two ends of the list, it compiles, but crashes.

    When a user goes forwards and backwards, the two options of forwards and backwards are not combined correctly.

    After trying to find an example of how it could be done most easily, I found THIS example.

    Could anyone point me in the right direction with this?

    Again, thanks for the great responses.

    Helped me out a loads!


Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 26
    Last Post: 07-05-2010, 10:43 AM
  2. circularly linked list
    By BlackOps in forum C Programming
    Replies: 0
    Last Post: 07-18-2009, 08:12 AM
  3. Link List math
    By t014y in forum C Programming
    Replies: 17
    Last Post: 02-20-2009, 06:55 PM
  4. Need help sorting a linked list. Beginner
    By scarlet00014 in forum C Programming
    Replies: 1
    Last Post: 09-27-2008, 06:16 PM
  5. singly linked list
    By clarinetster in forum C Programming
    Replies: 2
    Last Post: 08-26-2001, 10:21 PM