Search code examples
c++iteratorabstract-classnested-class

Abstract base class has abstract nested class


My code structure is as follows. I have a base abstract class which also has a nested abstract iterator. I inherit from the base class and i also inherit from the abstract base inherited class. To allocate the objects I use polymorphism:

i am getting errors like:

    In file included from newDataStore.h:6:0,
                     from newDataStore.cpp:1:
    ../HashTable/baseHashTable.h: In instantiation of ‘class BaseHashTable<std::basic_string<char>, User*>::iterator’:
    ../HashTable/hashTable.h:53:8:   required from ‘class HashTable<std::basic_string<char>, User*>::iterator’
    ../HashTable/hashTable.h:8:7:   required from ‘class HashTable<std::basic_string<char>, User*>’
    newDataStore.cpp:10:25:   required from here
    ../HashTable/baseHashTable.h:59:19: error: cannot allocate an object of abstract type ‘BaseHashTable<std::basic_string<char>, User*>::iterator’
      virtual iterator begin() const = 0;
                       ^
    ../HashTable/baseHashTable.h:27:8: note:   because the following virtual functions are pure within ‘BaseHashTable<std::basic_string<char>, User*>::iterator’:
      class iterator

Is my code structure appropriate? How to get rid of compiler errors : cannot allocate an object of abstract class?

Code:
// Instantiating the hash table
myUserHashTable = new HashTable<string, User*>; 

        template<class KeyType, class ValueType>
        class BaseHashTable // abstract class
        {
            class iterator
        {
        public:

            virtual const ValueType& operator*() const = 0;
            virtual iterator operator++() = 0;

            bool operator==(const iterator& other) const
            {
                return (row == other.row && parent_ == other._parent);
            }

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

            friend class BaseHashTable;

            virtual BaseHashTable<KeyType,ValueType>* getParent() const = 0;

            protected:
                iterator(int row, const BaseHashTable* parent)
                {
                    this->row = row;
                    parent_ = parent;
                }

                int row;
                const BaseHashTable* parent_;
        };

        virtual iterator begin() const = 0;
        virtual iterator end() const = 0;

protected:
int _size;

        };

        template<class KeyType, class ValueType>
        class HashTable : public BaseHashTable<KeyType, ValueType>
        {
            class iterator: public BaseHashTable<KeyType, ValueType>::iterator
        {
        public:
            const ValueType& operator*() const
            {
                return getParent()->hashTB[this->row]->at(col).second;
            }

            iterator operator++()
            {
                if (getParent()->hashTB[this->row]->size() > col+1)
                {
                    col += 1;
                    return *this;

                } else {

                    for (int i = this->row+1; i < this->parent_->_size; ++i)
                    {
                        if(getParent()->hashTB[i]->size() > 0)
                        {
                            this->row = i;
                            col = 0;
                            return *this;
                        }
                    }

                    this->row = getParent()->_size;
                    col = 0;
                    return *this;
                }
            }

            bool operator==(const iterator& other) const
            {
                return (this->col == other.col) ? BaseHashTable<KeyType, ValueType>::iterator::operator==(other) : false;
            }

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

            HashTable<KeyType,ValueType>* getParent() const
            {
                return ((HashTable<KeyType, ValueType>*)this->parent_);
            }

            friend class HashTable<KeyType, ValueType>;

            private:
                iterator(int row, int col, const BaseHashTable<KeyType, ValueType>* parent): BaseHashTable<KeyType, ValueType>::iterator(row, parent)
                {
                    this->col = col;
                }

                int col;
        };

        iterator begin() const
        {
            typename std::vector<std::pair<KeyType, ValueType> >::iterator it;

            int j = 0;
            for (int i = 0; i < this->_size; ++i)
            {
                if(hashTB[i]->size() > 0)
                    return iterator(j,0, this);
                j++;
            }

        }

        iterator end() const {
            return iterator(this->_size, 0, this);
        }

    protected:
    std::vector<std::pair<KeyType, ValueType> >** hashTB;
        };

        template<class KeyType, class ValueType>
        class DoubleHashingHashTable : public BaseHashTable<KeyType, ValueType>
        {
           class iterator {/*Implementation*/ } : public BaseHashTable<KeyType, ValueType>::iterator
           iterator begin() const {/*Implementation*/}
           iterator end() const {/*Implementation*/}
        };

Solution

  • virtual iterator begin() const = 0;
    

    An iterator cannot be an abstract class, or even a polymorphic class at all. Passing or returning by value requires creating a new, complete object of the iterator class. Any properties of a derived class will be stripped out by the "slice" operation.

    You might want type erasure, where a non-polymorphic object acquires polymorphic behavior by owning something polymorphic. The easy way of doing this involves the heap, though, and usually iterators (container iterators, anyway) avoid the complexity of heap allocation.