在“The C ++ Programming Language”一书中,作者给出了以下例子以及几个陈述:
如果仅仅返回引用并让用户决定如何处理它是不可接受的,那么定义一个用于读取和写入的运算符(例如[])是很困难的。
Cref,是帮助实现区分阅读和写作的下标运算符。
为何在阅读和写作时难以定义[]? 类Cref的定义如何帮助解决这个问题?
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);}
};
答案 0 :(得分:8)
如果您没有定义解决此问题的课程Cref
,那么您必须执行std::map
所做的事情:
template class <K,V> class map{
V& operator[](K const & key);
}
这将返回一个引用,必须由有效的内存位置支持,因此
std::map<string,string> m;
m["foo"];
assert(m.find("foo") != m.end());
断言将成功(意思是,"foo"
现在是地图中的有效密钥),即使您从未向m["foo"]
分配内容。
这种违反直觉的行为可以通过示例中的Cref
类来修复 - 它只有在您分配引用时才能执行适当的逻辑来创建m["foo"]
,并确保m.find("foo") == m.end()
如果您在尝试阅读不存在的m["foo"]
时没有执行某些任务。
同样,在您的String
类中(这是一个引用计数字符串 - 字符串共享其字符串数据,并且当您更改其数据与另一个字符串共享的字符串时会创建一个新副本),使用operator[]
读取字符时必须制作副本。使用Cref
类可以确保您在使用operator[]
写入时只制作副本。
答案 1 :(得分:3)
String s;
s[0] = 5;
将致电String::operator [](int)
,然后致电String::Cref::operator =(char)
。
然而,
String s;
char c = s[0];
将致电String::operator [](int)
,然后致电String::Cref::operator char()
。
阅读时,会调用String::Cref::operator char
,并且在调用String::Cref::operator =
时 - 这可以让您区分阅读和写作。
答案 2 :(得分:1)
为什么[]很难
被定义何时用于区分读写?
这是因为只要你有一个非const对象就会调用非const operator[]
,即使你是以只读方式使用它。