In the book of "The C++ Programming Language", the author gave the following example along with several statements:
Defining an operator, such as [], to be used for both reading and writing is difficult where it is not acceptable simply to return a reference and let the user decide what to do with it.
Cref, is to help implement a subscript operator that distinguishes between reading and writing.
Why [] is difficult to be defined when to be used for both reading and writing? How does the definition of class Cref help to solve this issue?
class String{
struct Srep;
Srep *rep;
public:
class Cref;
// some definitions here
void check (int i) const { if (i<0 || rep->sz<=i) throw Range( );}
char read( int i) const {return rep->s[i];}
void write(int i, char c){ rep=rep->get_own_copy(); rep->s[i]=c;}
Cref operator[] (int i){ check(i); return Cref(*this, i);}
char operator[] (int i) const{check(i); return rep->s{i];}
}
class String::Cref{
friend class String;
String& s;
int i;
Cref(String& ss, int ii): s(ss),i(ii) {}
public:
operator char( ) { return s.read(i);}
void operator=(char c){s.write(i,c);}
};
You should not overload operators like && and || . This is because these operators have a particular order in which an operand is evaluated. Since overloaded operators are just function calls, we cannot guarantee the order of evaluation of the operands.
Overloading Binary Operators Suppose that we wish to overload the binary operator == to compare two Point objects. We could do it as a member function or non-member function. To overload as a member function, the declaration is as follows: class Point { public: bool operator==(const Point & rhs) const; // p1.
Operator Overloading in C++ This means C++ has the ability to provide the operators with a special meaning for a data type, this ability is known as operator overloading. For example, we can overload an operator '+' in a class like String so that we can concatenate two strings by just using +.
In C++, as mentioned earlier the data type bool has been introduced to hold a boolean value, true or false. The values true or false have been added as keywords in the C++ language.
If you don't define a class Cref
that solves this issue, then you have to do what std::map
does:
template class <K,V> class map{
V& operator[](K const & key);
}
This returns a reference, which must be backed by a valid memory location, and therefore
std::map<string,string> m;
m["foo"];
assert(m.find("foo") != m.end());
The assertion will succeed (meaning, "foo"
is now a valid key in the map) even though you never assigned something to m["foo"]
.
This counterintuitive behavior can be fixed by the Cref
class in your example -- it can perform the appropriate logic to create m["foo"]
only when you assign to the reference, and ensure that m.find("foo") == m.end()
if you didn't perform some assignment when you tried to read the nonexistant m["foo"]
.
Likewise, in your String
class (which is a reference-counted string -- strings share their string data, and a new copy is created when you change a string whose data is shared with another string), you'd have to make a copy when using operator[]
to read characters. The use of the Cref
class, allows you to ensure that you only make a copy when using operator[]
to write.
String s;
s[0] = 5;
will call String::operator [](int)
and then String::Cref::operator =(char)
.
However,
String s;
char c = s[0];
will call String::operator [](int)
and then String::Cref::operator char()
.
When reading, String::Cref::operator char
is called, and when writing String::Cref::operator =
is called - this allows you to distinguish between reading and writing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With