有关运算符重载的问题
我有两个关于运算符重载的问题。
对于迭代器类型, operator->
如何重载? 假设它是一个class T
对象集合的迭代器,它应该返回什么值?
为什么operator++()
通过class T&
返回,而operator++(int)
通过class T
返回? 我明白这两个代表前缀增量和后缀增量。 但为什么回报价值的差异?
编辑:为Alf。 代码虽然功能尚未完成。 欢迎任何有待改进的建议。
#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
对于迭代器类型,operator->如何重载?
不是。 运算符 - >只能在类类型上重载。
如果你的意思是"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.
为什么operator ++()通过类T返回,而operator ++(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->
应该几乎总是返回一个指针类型。 在充当value_type
T
的迭代器时,它应该返回T*
。
在一些较罕见的情况下, operator->
可能返回一个不同的类类型,它也有一个operator->
member函数。
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&
类似的作用。
后增量表达式“x ++”的内置含义会增加变量,但会返回变量先前值的右值副本。 因此,大多数用户定义的重载都会返回原始值的副本(实际上可能永远不会成为参考)。
operator->
应该返回一个指向T
型的指针(即T*
)。
后缀增量必须返回值的一个副本,因为它执行增量但在使用该值之前。 前缀增量可以简单地在增加后返回*this
。
简单的实现可能如下所示:
T T::operator++(int)
{
T temp = *this;
++*this;
return temp;
}
T& T::operator++()
{
this->value += 1;
return *this;
}
链接地址: http://www.djcxy.com/p/50103.html
上一篇: Questions about operator overloading
下一篇: How to specify a "Custom Tool" for transforming a file in VS2010?