有关运算符重载的问题

时间:2011-01-19 03:16:06

标签: c++ operator-overloading

我有两个关于运算符重载的问题。

  1. 对于迭代器类型,operator->如何重载?假设它是class T个对象集合的迭代器,它应返回什么值?

  2. 为什么operator++()class T&返回operator++(int)时返回class T?我理解这两个代表前缀增量和后缀增量。但为什么回报值存在差异?

  3. 编辑:对于阿尔夫。虽然功能正常但代码并不完整。欢迎任何改进建议。

    #ifndef DHASH_H
    #define DHASH_H
    
    //#include <vector>
    #include <memory>
    #include <exception>
    #include <new>
    #include <algorithm>
    #include <functional>
    
    namespace MCol
    {
        template <typename KEY, typename VALUE, typename HASH_FUNCTION, typename KEY_COMP = std::equal_to<KEY> >
            class hash_container
            {
                private:
                    struct entry
                    {
                        KEY _k;
                        VALUE _v;
    
                        entry(const KEY& k, const VALUE& v)
                            :_k(k), _v(v)
                        {}
    
                        entry& operator=(const entry& e)
                        {
                            this->_k = e._k;
                            this->_v = e._v;
                        }
                    };
    
                private:
                    struct bucket
                    {
                        entry* a_Entries;
                        size_t sz_EntryCount;   
    
                        bucket()
                        {
                            sz_EntryCount = 0;
                            a_Entries = NULL;
                        }
    
                        ~bucket()
                        {
                            for(size_t szI = 0; szI < sz_EntryCount; ++szI)
                            {
                                a_Entries[szI].~entry();
                            }
                            free(a_Entries);
                        }
    
                        //Grow by 1. (Perhaps later try block increment. But wikipedia suggests that there is little difference between the two)
                        inline bool insert(const KEY& k, const VALUE& v) throw (std::bad_alloc)
                        {
                            if(find(k) != NULL)
                            {
                                return false;
                            }
                            a_Entries = static_cast<entry*>(realloc(a_Entries, sizeof(entry)*(++sz_EntryCount)));
                            if(a_Entries == NULL)
                            {
                                throw std::bad_alloc();
                            }
    
                            new (&a_Entries[sz_EntryCount - 1]) entry(k, v);
                            return true;
                        }
    
                        //Find entry, swap with last valid entry, remove if necessary.
                        inline bool erase(const KEY& k) throw(std::bad_alloc)
                        {
                            //Forwards or backwards? My guess is backwards is better.
                            entry* pE = a_Entries;
                            while(pE != a_Entries + sz_EntryCount)
                            {
                                if(pE->_k == k)
                                {
                                    break;
                                }
                                ++pE;
                            }
    
                            if(pE == a_Entries + sz_EntryCount)
                            {
                                return false;
                            }
    
                            //We don't need to swap if the entry is the only one in the bucket or if it is the last one.
                            entry* pLast = a_Entries + sz_EntryCount - 1;
                            if((sz_EntryCount > 1) && (pE != pLast))
                            {
                                pE = pLast;
                            }
    
                            a_Entries = static_cast<entry*>(realloc(a_Entries, sizeof(entry)*(--sz_EntryCount)));
                            if(a_Entries == NULL && sz_EntryCount > 0)
                            {
                                throw std::bad_alloc();
                            }
    
                            return true;
                        }
    
                        inline entry* find(const KEY& k) throw()
                        {
                            //Better implement a search policy.
                            entry* pE = a_Entries;
                            while(pE != a_Entries + sz_EntryCount)
                            {
                                if(pE->_k == k)
                                {
                                    break;
                                }
                                ++pE;
                            }
    
                            if(pE == a_Entries + sz_EntryCount)
                            {
                                return NULL;
                            }
    
                            return pE;
                        }
                    };
    
                    HASH_FUNCTION& _hf;
                    KEY_COMP _kc;
    
                    size_t sz_TableSize;
                    double d_MultFactor;                                            //Recalculate this as 1/sz_TableSize everytime sz_TableSize changes.
                    size_t sz_NextResizeLimit;
                    size_t sz_EntryCount;
                    double d_ExpectedLoadFactor;
                    double d_CurrentLoadFactor;
    
                    //If the load factor is relatively high (say >0.5 assuming sizeof(entry) == 2*sizeof(size_t)), it is more space efficient to keep a straight bucket array. But if the load factor is low, memory consumption would be lower if a pointer array of Entries is used here. But, because we would not be much concerned with a little additional memory being used when there are few entries, I think array of bucket objects is better. Further, it bypasses a pointer lookup. May have to reconsider is a situation where multiple hash tables are used (Perhaps as an array).
                    bucket* a_Buckets;
    
    
                    hash_container(const hash_container&);
                    hash_container& operator=(const hash_container&);
    
                    inline void calculateMultFactor() throw()
                    {
                        d_MultFactor = 1.0f/static_cast<double>(sz_TableSize + 1);
                        //sz_NextResizeLimit = static_cast<size_t>(d_ExpectedLoadFactor*sz_TableSize);
                        //Have a look at this.
                        //TODO
                    }
    
                    void resize(size_t szNewSize) throw(std::bad_alloc)
                    {
                        if(szNewSize == 0)
                        {
                            szNewSize = 1;
                        }
                        size_t szOldSize = sz_TableSize;
                        for(size_t szI = szNewSize; szI < szOldSize; ++szI)
                        {
                            a_Buckets[szI].~bucket();
                        }
    
                        a_Buckets = static_cast<bucket*>(realloc(a_Buckets, sizeof(bucket)*szNewSize));
                        if(a_Buckets == NULL)
                        {
                            throw std::bad_alloc();
                        }
                        //Unnecessary at the moment. But, just in case that bucket changes.
                        for(size_t szI = szOldSize; szI < szNewSize; ++szI)
                        {
                             new (&a_Buckets[szI]) bucket();
                        }
    
                        sz_TableSize = szNewSize;
                        calculateMultFactor();
                    }
    
                    inline bucket* get_bucket(const KEY& k) throw()
                    {
                        return a_Buckets + _hf(k, sz_TableSize);
                    }
    
                    inline bool need_resizing() const throw()
                    {
    
                    }
                public:
                    //typedef iterator void*;
                    //typedef const_iterator void*;
    
                    //iterator Insert(KEY& k, VALUE& v);
                    //VALUE& Find(Key& k);
                    //const VALUE& Find(Key& k);
                    //iterator Find(KEY k);
                    //const_iterator Find(KEY k);
                    //void Delete(KEY& k);
                    //void Delete(iterator it);
                    //void Delete(const_iterator it);
                    class iterator
                    {
                        private:
                            entry* p_Entry;
                            bucket* p_Bucket;
    
                            friend class bucket;
    
                        public:
                            iterator(entry* pEntry)
                                :p_Entry(pEntry)
                            {
                            }
    
                            iterator()
                            {
                                p_Entry = NULL;
                            }
    
                            iterator(const iterator& it)
                            {
                                this->p_Entry = it.p_Entry;
                            }
    
                            inline VALUE& operator*() const
                            {
                                return p_Entry->_v;
                            }
    
                            inline bool operator==(const iterator& it) const
                            {
                                return this->p_Entry == it.p_Entry;
                            }
    
                            inline bool operator!=(const iterator& it) const
                            {
                                return !(*this == it);
                            }
    
                            inline iterator& operator=(const iterator& it)
                            {
                                this->p_Entry = it.p_Entry;
                            }
    
                            inline VALUE* operator->() const
                            {
                                return &p_Entry->_v;
                            }
    
                            inline iterator operator++()
                            {
                                return *this;
                            }
    
                            inline iterator& operator++(int)
                            {
                                //WRONG!!!
                                //TODO : Change this.
                                return *this;
                            }
                    };
    
                private:
                    iterator _EndIt;
    
                public:
                    hash_container(HASH_FUNCTION& hf, size_t szTableSize = 1024, double dLoadFactor = 0.7f, KEY_COMP kc = KEY_COMP())throw(std::bad_alloc)
                        :_hf(hf), sz_TableSize(szTableSize), d_ExpectedLoadFactor(dLoadFactor), _kc(kc)
                    {
                        if(d_ExpectedLoadFactor < 0.1f)
                        {
                            d_ExpectedLoadFactor = 0.1f;
                        }
    
                        a_Buckets = NULL;
                        sz_TableSize = 0;
                        if(szTableSize == 0)
                        {
                            szTableSize = 1;
                        }
                        resize(szTableSize);
                        d_CurrentLoadFactor = 0.0f;
                        sz_EntryCount = 0;
    
                        _EndIt = iterator(NULL);
                    }
    
                    virtual ~hash_container()
                    {
                        for(size_t szI = 0; szI < sz_TableSize; ++szI)
                        {
                            a_Buckets[szI].~bucket();
                        }
                    }
    
                    inline iterator find(const KEY& k) throw()
                    {
                        bucket* pBucket = get_bucket(k);
                        return pBucket->find(k);
                    }
    
                    inline bool insert(const KEY& k, const VALUE& v) throw(std::bad_alloc)
                    {
                        bucket* pBucket = get_bucket(k);
                        bool bRet = false;
                        try
                        {
                            bRet = pBucket->insert(k, v);
                        }
                        catch(std::bad_alloc& e)
                        {
                            //What now?
                            throw e;
                        }
                        if(bRet == true)
                        {
                            ++sz_EntryCount;
                        }
                        return bRet;
                    }
    
                    inline VALUE& operator[](const KEY& k) throw(std::bad_alloc)
                    {
                        bucket* pBucket = get_bucket(k);
    
                    }
    
                    inline bool erase(const KEY& k) throw(std::bad_alloc)
                    {
                        bucket* pBucket =  get_bucket(k);
                        bool bRet = false;
                        try
                        {
                            bRet = pBucket->erase(k);
                        }
                        catch(std::bad_alloc& e)
                        {
                            throw e;
                        }
                        if(bRet == true)
                        {
                            --sz_EntryCount;
                        }
                        return bRet;
                    }
    
                    inline iterator end() const
                    {
                        return _EndIt;
                    }
    
                    inline size_t size() const
                    {
                        return sz_EntryCount;
                    }
    
                    inline size_t table_size() const
                    {
                        return sz_TableSize;
                    }
    
                    inline double current_load_factor() const
                    {
                        return d_MultFactor*static_cast<double>(sz_EntryCount);
                    }
    
                    inline double expected_load_factor() const
                    {
                        return d_ExpectedLoadFactor;
                    }
            };
    }
    
    #endif
    

3 个答案:

答案 0 :(得分:3)

0.1。 operator->几乎总是返回一个指针类型。当作为value_type T的迭代器时,它应该返回T*

在某些极少数情况下,operator->可能会返回不同的类类型,该类型也具有operator->成员函数。

0.2。任何形式的operator++必须返回的内容都没有技术要求,但通常的约定使它们最像内置意义。

class T {
public:
    // pre-increment
    T& operator++() { increment_me(); return *this; }
    // post-increment
    T operator++(int) { T copy(*this); increment_me(); return copy; }
    //...
};

预增量表达式++x的内置含义首先递增数字,然后将左值返回到递增的数字。返回类型T&的行为类似。

后增量表达式的内置含义&#39; x ++&#39;递增变量但返回变量的先前值的右值副本。因此,大多数用户定义的重载都会返回原始值的副本(实际上它永远不会成为引用)。

答案 1 :(得分:1)

  

对于迭代器类型,operator-&gt;如何?重载?

不是。运营商 - &gt;只能在类类型上重载。

如果您的意思是"How do I overload it to return an integer type".
那么答案是你做不到的。运营商的结果 - &gt;本身是非引用的,因此必须是指针类型(或对象(引用),它是具有重载operator-&gt;()的类类型。)

  

假设它是T类对象集合的迭代器,它应返回什么值?

它将返回指向T

的指针
struct Y { int a; };
std::vector<Y> plop(/* DATA TO INIT*/);

std::vector<Y>::iterator b = plop.begin();
b->a = 5; // here b.operator->() returns a pointer to Y object.
          // This is then used to access the element `a` of the Y object.
  

为什么operator ++()按类T&amp; while ++(int)按类T返回?

从技术上讲,他们可以退货。但通常它们是按照你的建议实施的 这是因为这些方法的标准实现:

class X
{
     public:
         // Simple one first. The pre-increment just increments the objects state.
         // It returns a reference to itself to be used in the expression.
         X& operator++()
         {
              /* Increment this object */
              return *this;
         }

         // Post Increment: This has to increment the current object.
         // But the value returned must have the value of the original object.
         //
         // The easy way to do this is to make a copy (that you return). The copy
         // has the original value but now is distinct from this. You can now use
         // pre-increment to increment this object and return the copy. Because
         // the copy was created locally you can not return by reference.
         X operator++(int)
         {
             X  copy(*this);
             ++(*this);
             return copy;
         }
};
  

我理解这两个代表前缀增量和后缀增量。但为什么回报值存在差异?

见上面代码中的评论。

答案 2 :(得分:0)

  1. operator->应返回指向T类型的指针(即T*)。

  2. 后缀增量必须返回值的副本,因为它在执行增量之前但在使用该值之前。增量后,前缀增量可以简单地返回*this

  3. 简单的实现可能如下所示:

    T T::operator++(int)
    {
        T temp = *this;
        ++*this;
        return temp;
    }
    
    T& T::operator++()
    {
        this->value += 1;
        return *this;
    }