我有两个关于运算符重载的问题。
对于迭代器类型,operator->
如何重载?假设它是class T
个对象集合的迭代器,它应返回什么值?
为什么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)
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)
operator->
应返回指向T
类型的指针(即T*
)。
后缀增量必须返回值的副本,因为它在执行增量之前但在使用该值之前。增量后,前缀增量可以简单地返回*this
。
简单的实现可能如下所示:
T T::operator++(int)
{
T temp = *this;
++*this;
return temp;
}
T& T::operator++()
{
this->value += 1;
return *this;
}