Thread: implementing iterator in copy ctor

  1. #16
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by anon View Post
    You should change the end () method, to make it more efficient and not crash with empty lists

    Code:
    iterator end() const { return iterator(0); };
    I also suspect that if you only allow readonly access through the iterator, it should be called const_iterator more properly.
    Of coruse, agree on both accounts - although I suppose there's no reason why this design couldn't allow access through the iterators - I just didn't implement that. I just wanted to see if I could implement something that made use of iterators.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  2. #17
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    hey thanx matsp. that's exactly what i was hoping for. quickly skimming through the code i guess what was most confusing to me was how to construct the iterator class. the fact that Node was private and Iterator was public, and trying to allow iterator access to the private node was confusing as all hell. One bit of confusion I see is this:
    Code:
    iterator(Node* n = 0) : curr(n) {}
    but then you have:
    Code:
    iterator begin() const {return iterator(head);}
    if in the ctor curr is initialized to 0, how then can you specify to initialize it to head?

    Me = confused

  3. #18
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Code:
    iterator(Node* n = 0) : curr(n) {}
    says that "iterator is constructed using a node n, which has a default value of 0".
    So we can do:
    iterator i; // curr = 0;
    or
    iterator i(43); // curr = 43 [ignoring that the type isn't a Node * for now].

    So, when we do
    Code:
    iterator begin() const {return iterator(head);}
    the iterator is constructed with the value of head, not with zero.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #19
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    gotya. thankx.

    also, let's say I have a member function display(). It is possible to use an iterator to traverse the list? I mean, obviously I need a list object or pointer to call begin() or end(), so can "this" call those functions?

    Code:
    template <typename T> void List<T>::display(){
    
    for(mlist<int>::iterator i = this->begin(); i != this->end(); i++) {
    			std::cout << "node = " << *i << std::endl;
    		}
    }
    obviously that won't work, but is there a simple workaround to get it working?

  5. #20
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    haha! actually that DID work!

  6. #21
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Code:
    template <typename T> void List<T>::display(){
    
    for(mlist<int>::iterator i = this->begin(); i != this->end(); i++) {
    			std::cout << "node = " << *i << std::endl;
    		}
    }
    Some selective replacements will probably make it work. The red bit is quite obviously from my code and should most likely be "List<T>" instead.

    since *i (in my case) returns an object of type T, you also need to have operator<<(ostream, T) [not that you would have to be responsible for writing that, but it needs to exist somewhere].

    Edit: And it would probably work just as well without the "this->" because that is the default for any function within the class.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #22
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    another thing i'd like to add, and i don't understand why, but moving the private node before the public interface, cleared up some of the errors i was getting like:


    SOME_ERROR: cannot declare someFunc() of type Node with no type

  8. #23
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by sh3rpa View Post
    another thing i'd like to add, and i don't understand why, but moving the private node before the public interface, cleared up some of the errors i was getting like:


    SOME_ERROR: cannot declare someFunc() of type Node with no type
    Yes, you need to declare that the class by name of Node exists before you can say "this returns something of type Node".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #24
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    oh geez, after playing around with this for a while, i'm confused about something.

    in my design i had this:
    Code:
    list() : head(new Node(T())), tail(new Node(T())) {}
    matsp designed this:
    Code:
    list() : head(0), tail(0) {}
    i'd like to have Node pointers "next" and "prev" so i can iterate in both directions from either the head or tail respectively.

    What confuses me, is do the pointers "head" and "prev" actually have both "next" and "prev" pointers?

    I mean, if they are just Node pointers, don't they only point to one thing? But an actual instance of a Node would have both "next" and "prev"?

    This confusion came about because when I tried to implement matsp's design with a "prev" pointer as well, i ran into problems.

    Please help me understand what the porblem is?

  10. #25
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Do you want to loop circularly? I assume you don't. You only need tail->next and head->prev to point to valid locations if you do.

    Making the head and the tail null indicate that the list is empty. In your version, you created two nodes, one for the head and one for the tail. But that doesn't really make sense if you want to start with an empty list. The only reason to do that would be if you were using head and tail as sentinal nodes and you add logic to the rest of your code to ignore their values.

    The more common solution is to just assign pointers as necessary. When a new node is created the first time, you point head and tail to it and let the prev and next pointers point to 0. When a second node is added, you re-assign tail and then reassign the prev and next pointers as necessary.

  11. #26
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Sherpa sent a PM asking how I would implement pop_back() and pop_front(), so I had to do that then...

    This is the additional code to what I posted earlier:
    Code:
    // in class declaration:
    	void pop_back();
    	void pop_front();
    ...
    // And here's the implementation.
    template <typename T>
    void mlist<T>::pop_back()
    {
    	Node *curr(head);
    	Node *prev(0);
    
    	while (curr != tail) {
          prev = curr;
    	  curr = curr->next;
    	}
    	if (prev) {
    		delete prev->next;
    		prev->next = 0;
    		tail = prev;
    	} else {
    		head = tail = 0;
    	}
    }
    
    template <typename T>
    void mlist<T>::pop_front()
    {
    	if (head) {
    		Node *temp(head);
    		head = head->next;
    		delete temp;
    		if (!head) tail = 0;
    	}
    }
    As you see, the pop_back() is rather ugly with a big while-loop, since I only implemented a single-linked list, I can't use the tail to find the previous node, I have to walk all the way from the head to the end of the list. But it's an implementation. If it's a common operation to remove data from the back of the list, it's obviously a better idea to make the list double-linked [but that also makes the entire implementation more complex, and I wanted something that was trivial to implement - more code just makes it harder to follow].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #27
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    In a singly linked list, you usually wouldn't implement pop_back at all, just as your iterators are not bidirectional.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  13. #28
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by CornedBee View Post
    In a singly linked list, you usually wouldn't implement pop_back at all, just as your iterators are not bidirectional.
    Yes. But if someone asks you "how do I implement this", it's not a good answer to say "well, I wouldn't" - or maybe it is, because it certainly wouldn't be a good idea to use this method if there are thousands or millions of entries in the linked list - very bad performance indeed.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #29
    Registered Abuser
    Join Date
    Sep 2007
    Location
    USA/NJ/TRENTON
    Posts
    127
    thanx for answering the pm matsp. anyways, the problem i was having w/my pop_back func was something really stupid.

    I was looking through your header, while creating my class, but I added a prev pointer in mine. Well, suffice to say, i forgot to assign my prev pointers in my push functions, so when it got to pop.... you can see where I'm going with this...


    thankx everyone!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Functions which return a reference
    By Stonehambey in forum C++ Programming
    Replies: 10
    Last Post: 07-05-2008, 01:43 PM
  2. Question about linked-list copy ctor
    By dudeomanodude in forum C++ Programming
    Replies: 8
    Last Post: 03-06-2008, 10:29 AM
  3. calling copy constructor from template
    By Ancient Dragon in forum C++ Programming
    Replies: 3
    Last Post: 09-28-2005, 01:54 PM
  4. dynamic memory alloccation & returning objects
    By haditya in forum C++ Programming
    Replies: 8
    Last Post: 04-21-2005, 11:55 PM
  5. Copy Constructor Help
    By Jubba in forum C++ Programming
    Replies: 2
    Last Post: 11-07-2001, 11:15 AM