C++ allows operator overloading. Among them, the square-bracket operator "[]", which usually denotes subscripting, is naturally overloaded to provide "get or set some attribute" semantics.
The simplest approach is to return an address; any operations performed on it are completely exposed.
class Opol {
public:
Opol() {
memset(a,0,10);
}
int & operator [] (int k) {
return a[k];
}
private:
int a[10];
}The code above returns an address. When you operate on the returned result, the corresponding memory value is modified, and the value inside class Opol is updated accordingly.
But that obviously can't satisfy all our eccentric requirements. For example, if I want to build a file-based dictionary, the getter must fetch the corresponding content from a file, while the setter must write the corresponding value back to the file. An ordinary address reference can't accomplish this; we clearly need something more powerful.
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;
};The simplest approach is to write a set(k, v) and a get(k) as shown above. Once those two are written, the functionality is already there, but it's so ugly that we can hardly continue coding. I know there must be a better way to implement this.
Turning our attention back to operator overloading: operators are not forced to "must return type xxx". A simple method is to build an intermediate class internally and implement set and get inside it.
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;
};As shown above, simply add an Agent, and the returned result is actually an Agent. Because an int is "needed", Agent's operator int() method is called to implement the "getter". When it appears as an lvalue, the right-hand side invokes void operator=(int v), implementing the setter.
This way, we can use square brackets freely.
Opol o;
o[1] = 3;
printf("o_1 is : %d\n",static_cast<int>(o[1]));Of course, this is still not comprehensive. For example, if I try to use o[1]++, it will break. Adding two more functions to class Agent will achieve this.
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;
}At this point, statements like the following can be used easily:
printf("o_1++ is : %d\n",static_cast<int>(o[1]++));
printf("++o_1 is : %d\n",static_cast<int>(++o[1]));The final output is as follows:
$ ./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 : 5Finally, a small question: how do you implement Opol[][]?
Comments · 1