2011-01-19 18 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

यह होमवर्क जैसा दिखता है। कृपया "होमवर्क" शब्द शामिल करने के लिए अपने प्रश्न शीर्षक में संशोधन करें। –

+3

मुझे होमवर्क की तरह दिखता नहीं है। ऐसा लगता है कि वह इटरेटर को कार्यान्वित करने के बारे में उत्सुक है। –

+1

@ अल्फ पी। स्टीनबाच: होमवर्क नहीं। मैं हैश टेबल के लिए इटरेटर्स को कार्यान्वित कर रहा हूं। – nakiya

उत्तर

1

एक पुनरावर्तक प्रकार के लिए, ऑपरेटर-> अधिभार कैसे होता है?

यह नहीं है। ऑपरेटर-> केवल वर्ग प्रकारों पर अधिभारित किया जा सकता है।

यदि आपका मतलब "How do I overload it to return an integer type".
तब उत्तर है कि आप नहीं कर सकते हैं।ऑपरेटर का परिणाम-> स्वयं को संदर्भित किया गया है और इस तरह एक सूचक प्रकार होना चाहिए (या एक वस्तु (संदर्भ) जो अधिभार ऑपरेटर ->() के साथ एक वर्ग प्रकार है)।

यह मानना ​​चाहिए कि यह कक्षा टी वस्तुओं के संग्रह के लिए एक पुनरावर्तक है?

यह एक सूचक

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. 

टी करने के लिए वापस आ जाएगी क्यों, जबकि ऑपरेटर ++ (int) वापसी वर्ग टी द्वारा ऑपरेटर ++() वर्ग टी & से वापसी?

तकनीकी रूप से वे कुछ भी वापस कर सकते हैं। लेकिन आमतौर पर वे सुझाए गए अनुसार लागू होते हैं।
यह इन तरीकों में से मानक कार्यान्वयन की वजह से है:

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; 
} 
+0

मैं 'ऑपरेटर->' के बारे में उलझन में हूं। यदि यह 'कक्षा टी *' देता है, तो इसका मतलब यह नहीं है कि उपयोग इस तरह होना चाहिए: 'संग्रह :: इसे पुनरावर्तक; it.operator ->() -> सदस्य = एक्स; '? या क्या मुझे यह गलत मिला है? – nakiya

+1

ऑपरेटर को स्पष्ट रूप से कॉल न करें। बस 'it-> सदस्य = x' लिखें। संकलक स्वचालित रूप से टी * प्राप्त करने के लिए 'ऑपरेटर->' पर कॉल करता है, और उसके बाद अंतर्निहित '->' तर्क लागू करता है। –

+0

@ कार्ल कंचटेल: यह मेरा जवाब है। मैंने सोचा कि वह अतिरिक्त '->' गायब हो गया है। – nakiya

3

.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 का अर्थ पहले नंबर वृद्धि कर देता है और फिर बढ़ती संख्या के लिए एक lvalue देता है। T& का रिटर्न प्रकार इसी प्रकार कार्य करता है।

पोस्ट-वृद्धि अभिव्यक्ति 'x ++' का अंतर्निहित अर्थ चर को बढ़ाता है लेकिन चर के पिछले मान की एक रैल्यू प्रति देता है। इसलिए अधिकांश उपयोगकर्ता परिभाषित अधिभार मूल मूल्य की प्रतिलिपि लौटाते हैं (जो व्यावहारिक रूप से संदर्भ नहीं हो सकता है)।

+0

वही सवाल @ ज़ूबा: मैंने ऑपरेटर-> के बारे में उलझन में है। यदि यह कक्षा टी * देता है, तो इसका मतलब यह नहीं है कि उपयोग इस तरह होना चाहिए: 'संग्रह :: इसे पुनरावर्तक; it.operator ->() -> सदस्य = एक्स; '? या क्या मुझे यह गलत मिला है? – nakiya

+0

@nakiya: प्रत्येक अधिभारित ऑपरेटर ('new',' delete' और उनके सर रिश्तेदारों को छोड़कर) को ऑपरेटर का उपयोग करके या 'ऑपरेटर' कुंजी शब्द का उपयोग करके लंबे प्रारूप में संक्षिप्त रूप में बुलाया जा सकता है। जैसे 'ऑपरेटर + (एक्स, वाई)' 'x + y' के लिए लंबा है। 'it.operator ->() -> सदस्य' आपके 'ऑपरेटर->' के लंबे रूप का उपयोग करने का एक सही तरीका है और 'it-> सदस्य' आपके' ऑपरेटर-> के संक्षिप्त रूप का उपयोग करने का एक सही तरीका है। '। – aschepler

संबंधित मुद्दे