Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call const_iterator for non const class? [duplicate]

I read some other threads related to this issue but non offered a solution for my problem. I hope you guys can give me ideas or advices.

I'm trying to implement this class named Map. it should contain 2 iterators - iterator and const_iterator.

I have them implemented - iterator inherits from const_iterator, and in the Map class I have the following functions:

iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;

we were given an example file to see what is required to implement. in there, there is the following code:

Map<std::string,int> msi;

...

// print map
for(Map<std::string,int>::const_iterator it = msi.begin(); it != msi.end(); ++it) {
// more stuff here
}

since msi is a non-const Map instance, msi.begin() calls to iterator begin() and not const_iterator begin() const, resulting in unintended behaviour.

Assuming the example file is okay, how do I make it so msi.begin() calls the correct const_iterator function? (considering it, the iterator, is of type const_iterator).

EDIT: regarding the talk about the auto-conversion, i decided to add my iterator classes, please point out my mistake.

class Map {

    //...

    public:

        class const_iterator {

        private:

            Node* currNode;

        public:


            const_iterator(Node* cur_node = NULL) : currNode(cur_node) {}

            const_iterator& operator++() {
                currNode = currNode->next;
                return *this;
            }

            const_iterator operator++(int) {
                const_iterator old = *this;
                ++(*this);
                return old;
            }

            bool operator!=(const_iterator const& curr) {

                return !(*this == curr);
            }

            string operator*() {
                // this might cause memory leak
                string toString(this->currNode->key);
                std::stringstream s;
                int tmp = this->currNode->value;
                s << tmp;
                string secondString(s.str());
                toString = toString + ":" + secondString;
                return toString;
            }

            bool operator==(const_iterator const& curr) {
                return this->currNode == curr.currNode;
            }


            void operator=(const_iterator target) {
                this = target;
            }

            //void operator=(Node* target) {
            //    this->currNode = target;
            //}
        };

        class iterator : public const_iterator {

        private:

            Node* currNode;

        public:

            iterator(Node* cur_node = NULL) : currNode(cur_node) {}

            iterator& operator++() {
                currNode = currNode->next;
                return *this;
            }

            iterator operator++(int) {
                iterator old = *this;
                ++(*this);
                return old;
            }

            bool operator==(iterator const& curr) {
                return *this == curr;
            }

            bool operator!=(iterator const& curr) {

                return !(*this == curr);
            }

            string operator*() {
                // this might cause memory leak
                string toString(this->currNode->key);
                std::stringstream s;
                int tmp = this->currNode->value;
                s << tmp;
                string secondString(s.str());
                toString = toString + ":" + secondString;
                return toString;
            }

            void operator=(iterator target) {
                this = target;
            }

        };

        //..
}
like image 788
Zephyer Avatar asked Mar 21 '23 01:03

Zephyer


1 Answers

C++11 standard containers add cbegin and cend for that purpose. Lacking that, you can obviously always cast your object to const& explicitly to get a const view on the object.

More fundamentally, however, there’s no reason why your iterator shouldn’t support an automatic conversion to const_iterator. Like that, you’d not need to change the client code at all. In fact, your code should already support this if, as you’ve said, iterator inherits from const_iterator.

However, the code you’ve posted contains several errors. Firstly, operator= is wrong, and you should have received an error for it. The corrected version is:

void operator=(const_iterator target) {
    currNode = target.currNode;
}

More importantly, your inheritance makes no sense. True, you do inherit iterator from const_iterator but your code pretends that this never happened – iterator completely reimplements its parent class and doesn’t relate to it in any way.

iterator should rather look something like this:

class iterator : public const_iterator {
public:
    iterator(Node* cur_node = NULL) : const_iterator(cur_node) {}
};

This requires of course that currNode is declared protected in const_iterator. That class is also completely useless (but so is yours, at the moment) since it doesn’t add any functionality to the const_iterator class. You need to implement an operator* which allows modifying its value. Your current code fundamentally doesn’t allow this since it returns a newly created string rather than (something akin to) a reference to a map value.

Furthermore, it’s unclear how the const_iterator class gets ahold of a non-const Node pointer in the first place. That shouldn’t be possible: after all, it gets the pointer from a const Map.

like image 143
Konrad Rudolph Avatar answered Apr 01 '23 15:04

Konrad Rudolph