重载中括号操作符, 执行复杂操作

C++可以重载运算符, 其中中括号 “[]”, 通常在其它地方用于表示下标的操作符, 重载自然也是用于”获得或者设置某个属性”这么个功能.

最简单的莫过于返回一个地址, 做的操作自然都被看光了.

class Opol {
public:
Opol() {
memset(a,0,10);
}

int & operator [] (int k) {
return a[k];
}
private:
int a[10];
}

如上的代码返回的是一个地址, 当你对返回的结果进行操作的时候, 对应内存的值被修改, class Opol 中对应的值自然也被随之修改了。

但是这个显然不能满足我们的各种奇葩的要求. 比如我要做一个基于文件的字典, getter 要做的事情是从文件中获得对应的内容, 而 setter 要做的事情是将对应的值写回文件. 而普通的地址引用是无法完成这样的操作的, 我们显然需要一些更有力的操作.

class Opol {
public:
int get(int k) const {
return a.at(k);
}

void set(int k, int v) {
if (a.find(k) == a.end()) {
a.insert(std::pair(k, v));
} else {
a[k] = v;
}
}
private:
map a;
};

最简单的莫过于如上那样写个 set(k, v), get(k) 两个一写, 功能就已经实现了, 但是这个实在是太丑了, 以至于我们几乎都无法再继续写代码了, 我知道, 一定有更好的办法来实现这些的。

目光重新移回重载操作符. 操作符并没有强制要求 “必须返回 xxx 类型”. 一个简单的方法是内部构建一个中间 class, 然后再内部实现 set 和 get.

class Opol {
private:
class Agent {
public:
Agent(Opol & m_o, int m_k) : o(m_o), k(m_k) {}

// The operator int() allows implicit and explicit casting to ints.
// This means that when the CB functionality cannot be applied,
// it tries if it works for an int (implicit) and you can directly
// cast a Agent object to an int value (by using int(x), (int)x,
// dynamic_cast(x) or static_cast(x)).
// ref: http://www.cplusplus.com/forum/beginner/33566/
operator int() const {
printf("get : %d %d\n", k, v);
o.set(k, v);
}

private:
Opol &o;
int k;
};

public:
int get(int k) const {
return a.at(k);
}

void set(int k, int v) {
if (a.find(k) == a.end()) {
a.insert(std::pair(k, v));
} else {
a[k] = v;
}
}

Agent operator[] (int k) {return Agent(*this, k);}

private:
map a;
};

如上, 简单的添加一个 Agent, 然后返回的结果其实是一个 Agent, 而因为”需要”的是一个 int, 然后调用 Agent 的 operator int() 方法实现 “getter” 的功能. 而当它作为左值的时候, 右值会调用 void operator = (int v), 实现 setter 的功能.

这样我们就可以随便使用中括号了.

Opol o;
o[1] = 3;
printf("o_1 is : %d\n",static_cast(o[1]));

当然, 这个还不全面, 比如我若打算用个 o[1]++ 那就废了. class Agent 中再添加两个 function 即可达成目标.

int operator++() {
printf("pre-increment\n");
o.set(k, o.get(k) + 1);
return *this;
}

int operator++(int) {
printf("post-increment\n");
int val = o.get(k);
o.set(k, val + 1);
return val;
}

这时候就可以很容易的使用如下语句了

printf("o_1++ is : %d\n",static_cast(o[1]++));
printf("++o_1 is : %d\n",static_cast(++o[1]));

最后输出结果如下:

$ ./a.out
set : 1 -> 3
get : 1 <- 3
o_1 is : 3
post-increment
o_1++ is : 3
pre-increment
get : 1 <- 5
++o_1 is : 5

最后, 一个小问题: 怎样实现Opol[][] ?