2011-01-19 67 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 ++」增加變量,但返回變量的前值右值的副本。因此,大多數用戶定義的重載返回原值(可實際上永遠是一個參考)的副本。