最近,我决定在C++实施一棵红黑树作为个人项目。这已经存在并且实施起来很痛苦,几乎每行都发生段错误,但这既是红黑树(我在学校学习)的良好练习,也是C++(这总是很好的改进)。我让我的树处理 get、set、insert和 remove,世界一切都很好。但是,我缺少 C# 和 python 的简单语法,其中人们可以只索引地图,而不是使用笨拙的方法和点语法。在实践中,我们可以写点,这没什么大不了的,但这已经是一个实验,所以为什么不尝试让使用更容易(和更酷)呢?std::map
std::set
map[key] = value
C++ 允许我们覆盖自定义类型的几乎所有运算符,例如 、、 和 订阅运算符 .C/C++ 中的正常行为是像 中一样自动执行指针算术和取消引用,但在语义上,它已成为索引(或下标,因此得名)数组、列表和映射的同义词。为类型重载此运算符的语法是使用签名创建方法。单数参数对应于方括号内的内容。此方法可以在地图上调用为+
-
>
=
[]
pointer[5] = *(pointer+5)
<returnType> operator[](<paramType> <param>)
mymap
<returnType> v = mymap[<param>];
像我们想要的那样实现订阅运算符有点困难,因为它需要提供两个功能,获取和设置。我们不能直接引用值字段,因为有特定的getter和setter,我们不希望它公开。因此,我们需要根据运算符的使用方式和位置来改变行为。 应该调用 getter 方法,而应该调用 setter/insertion。确实没有办法使两个具有不同行为的重载(您可以自己尝试看看为什么),因此我们必须提出一个更棘手的解决方案。我们可以返回一个“代理”对象,该对象重载运算符并根据对其执行的操作调用正确的方法。int val = mymap[key];
mymap[key] = val;
operator[]
rbtree<K, V>::getSetProxy operator[](K key) { return getSetProxy(this, key); }
现在调用变成了又变成了int val = mymap[key];
int val = getSetProxy;
mymap[key] = val;
getSetProxy = val;
此代理对象将根据用户尝试执行的操作处理获取和设置。代理的构造接受对树的引用,以便它可以执行操作,以及键,以便它知道在哪里获取/设置/插入。
设置的情况相当简单。我们将使用 assignment 运算符进行设置,因此我们可以重载它以调用 setter 方法。=
class getSetProxy { rbtree<K, V> *theTree; K key; ... void operator=(V value) { if (!theTree->setValue(key, value)) { theTree->insert(key, value); } } ... }
赋值右侧的值在树中的键处设置或插入。
Get 有点棘手。C++中有一个运算符称为隐式强制转换运算符,签名 其中是要强制转换为的类型,只是表示此函数无法修改类。每当需要强制转换为其他类型时C++都会调用此函数。operator T() const;
T
const
myclass myobj = myobj(); int myint = myobj; // ^^^^^ // implicit cast int() is called
所以在 中,将被调用。我们可以重载它以获得这样的功能:T val = getSetProxy;
operator T()
class getSetProxy { rbtree<K, V> *theTree; K key; ... operator V() const { return theTree->getValue(key); } ... }
请原谅我滥用符号和平等以及所有即将发生的数学。
V value = mymap[key];
→
→→V value = getSetProxy{key = key};
V value = getSetProxy.operatorV();
V value = mymap.getValue(key);
mymap[key] = value;
(为了简单起见,假设键不在地图中)
→
→→getSetProxy{key = key} = value;
getSetProxy{key = key}.operator=(value);
mymap->setValue(key, value)
C++很有趣,因为您可以真正使语言成为您自己的语言。我能够采用笨拙的语法,并在几行(尽管有点棘手)代码中使其更熟悉。整个项目的代码,包括这个下标部分