2011-01-19 159 views
6

我有两个关于操作员重载的问题。有关操作员超载的问题

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

  2. 为什么operator++()回报由class T&operator++(int)返回由class T?我明白这两个代表前缀增量和后缀增量。但为什么回报价值的差异?

编辑:对于阿尔夫。代码虽然功能尚未完成。欢迎任何有待改进的建议。

#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 
+0

这看起来像功课。请修改您的问题标题以包含“家庭作业”一词。 – 2011-01-19 03:20:54

+3

看起来不像我的功课。看起来他很好奇如何实现迭代器。 – 2011-01-19 03:23:08

+1

@ Alf P. Steinbach:不是作业。我正在为哈希表实现迭代器。 – nakiya 2011-01-19 03:24:50

回答

1

对于迭代器类型,如何操作符 - >过载?

不是。运算符 - >只能在类类型上重载。

如果你的意思是"How do I overload it to return an integer type".
那么答案是你不能。operator->的结果本身是取消引用的,因此必须是一个指针类型(或者是一个具有重载操作符 - >())的类类型的对象(引用)。

它应该返回什么值,假设它是一个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. 

为什么运营商++()由T级&回报,同时操作++(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; 
     } 
}; 

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

见上面代码中的注释。

0
  1. operator->应当返回一个指针键入T(即T*)。

  2. 后缀增量有返回值的副本,因为它执行的增量,但已经使用前值。增量后,前缀增量可以简单地返回*this

简单的实现方式可以是这样的:

T T::operator++(int) 
{ 
    T temp = *this; 
    ++*this; 
    return temp; 
} 

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

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

在一些罕见的情况下,operator->可能返回不同类型,它也具有operator->成员函数。

.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&的行为类似。

内置增量后表达的意思是“X ++”增加变量,但返回变量的前值右值的副本。因此,大多数用户定义的重载返回原值(可实际上永远是一个参考)的副本。