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<int, int>(k, v));
        } else {
            a[k] = v;
        }
    }
 private:
    map<int, int> 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<int>(x) or static_cast<int>(x)).
        // ref: http://www.cplusplus.com/forum/beginner/33566/
        operator int() const {
            printf("get : %d <- %d\n", k, o.get(k));
            return o.get(k);
        }

        void operator = (int v) {
            printf("set : %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<int, int>(k, v));
        } else {
            a[k] = v;
        }
    }

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

 private:
    map<int, int> 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<int>(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<int>(o[1]++));
printf("++o_1 is : %d\n",static_cast<int>(++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[][] ?

Categories: Code

Yu

Ideals are like the stars: we never reach them, but like the mariners of the sea, we chart our course by them.

1 Comment

arm · December 17, 2014 at 12:50

Google Chrome 32.0.1700.107 Google Chrome 32.0.1700.107 Windows 7 Windows 7

很实用的代码

Leave a Reply

Your email address will not be published. Required fields are marked *