这个问题与另一个问题密切相关:当你访问一个不存在的值而不是赋值时,你想要什么行为?换句话说,当你写的时候你想发生什么:
std::cout << hashTable[string] << std::endl;
并且string
不存在于表中?
有两种可能的方法:您可以将其视为错误,然后抛出异常、中止或类似的东西;或者您可以返回某种默认值,使用默认构造函数构建,或者由客户端早些时候提供。
标准 map 和 unordered_map 采用第二种方法,使用默认构造函数构造一个新值。这允许一个非常简单的解决方案:如果operator[]
不存在,则插入它,并使用默认值对其进行初始化。然后返回对它的引用;
hashTable[string] = newString;
通过对已经存在的值的引用进行分配。
在许多用例中,第一种方法会更可取(可能使用
contains
函数,因此您可以预先测试它是否operator[]
会找到某些东西)。要实现第一种方法,您必须首先为每种访问类型实现特定功能:
template <typename Key, typename Value>
class HashTable
{
public:
Value* get( Key const& key ) const;
void set( Key const& key, Value const& value );
};
(我通常会公开这些信息;没有理由禁止客户使用它们。)
然后,您定义operator[]
返回一个代理,如下所示:
template <typename Key, typename Value>
class HashTable
{
public:
class Proxy
{
HashTable* myOwner;
Key myKey;
public:
Proxy( HashTable* owner, Key const& key )
: myOwner( owner )
, myKey( key )
{
}
operator Value const&() const
{
Value const* result = myOwner->get( myKey );
if ( result == NULL ) {
// Desired error behavior here...
}
return *result;
}
Proxy const& operator==( Value const& value ) const
{
myOwner->set( myKey, value );
return *this;
}
};
Value* get( Key const& key ) const;
void set( Key const& key, Value const& value );
Proxy operator[]( Key const& key )
{
return Proxy( this, key );
}
};
因此,当你写:
hashTable[key] = newString;
,代理operator=
将调用hashTable.put( key, newString )
;但是,在其他情况下,它将调用代理上的隐式类型转换,该代理调用hashTable.get( key )
.
在某些情况下,即使您希望返回默认值,最好使用此解决方案:该get
函数不需要向哈希表中插入任何内容,因此该表不会填满所有未命中的值,你可以重载operator[]
on ,所以你也可以在哈希表const
上使用它。const
此外,它不需要值类型具有默认构造函数。
相对于标准中使用的解决方案,它确实有一个缺点。因为你不能重载operator.
,你不能让代理表现得像一个引用,比如:
hashTable[string].someFunction();
不工作。一种解决方法是operator->
在代理中重载,但这会导致语法有些不自然:
hashTable[string]->someFunction(); // But the hash table contains
// values, not pointers!!!
a
(不要被隐式转换为引用所误导。在表达式中
不会考虑隐式转换a.b
。)