在我们的带引用计数的string类中对于operator[]的操作分为cosnt和非const,const对象调用不会修改string对象的值的函数,并假定非const的operator[]调用时会修改string对象的值,因此非const的operator调用时不管实际上有没有修改string对象的值,都需重新拷贝一份string对象。通过代理类延迟对operator[]的操作可以对读写操作进行识别。 以下为添加代理类的String类的部分实现:
#pragma once #include <cstring> #include <iostream> using namespace std; class String { // class to be used by application developers public: class proxychar { public: proxychar(const int index, String &rhl); proxychar &operator= (const proxychar &rhl); proxychar &operator= (const char c); friend ostream& operator<<(ostream &os, const proxychar &rhl) { return os << rhl.m_str.value->data[rhl.m_index]; } private: String &m_str; int m_index; }; String(const char *value = ""); const proxychar& operator[](int index) const; proxychar& operator[](int index); friend proxychar; String(const char *value = ""); const proxychar& operator[](int index) const; proxychar& operator[](int index); private: // class representing string values struct StringValue : public RCObject { char *data; StringValue(const char *initValue); StringValue(const StringValue& rhs); void init(const char *initValue); ~StringValue(); }; RCPtr<StringValue> value; }; const String::proxychar& String::operator[](int index) const { return String::proxychar(index,const_cast<String &>(*this)); } String::proxychar& String::operator[](int index) { return String::proxychar(index, *this); } String::proxychar::proxychar(const int index, String &rhl):m_str(rhl),m_index(index) { } String::proxychar& String::proxychar::operator = (const proxychar &rhl) { if (m_str.value->isShared()) { m_str.value = new StringValue(m_str.value->data); } m_str.value->data[m_index] = rhl.m_str.value->data[rhl.m_index]; return *this; } String::proxychar & String::proxychar::operator=(const char c) { if (m_str.value->isShared()) { m_str.value = new StringValue(m_str.value->data); } m_str.value->data[m_index] = c; return *this; }在这里,在调用operator[]时,我们不直接返回char,而是返回一个char的代理类的对象,该类持有string类的引用,并且封装了char所需要的大部分操作,然后在对char的操作,比如operator=中对string进行拷贝,在operator<<中直接返回string的下标.
当需要输出String[index]时,String类先返回一个proxychar对象,然后调用proxychar类的operator<<操作符,此时String类不需要进行拷贝。 当需要修改String[index]时,String类先返回一个proxychar对象,然后调用proxychar类的operator=操作符,此时String类判断是否需要进行拷贝,然后进行赋值
但是这个类存在一些缺陷: 1.需要自己实现原始类(这里是char类型)的所有操作,如operator<<操作符,operator=操作符,operator>>,operator&()操作符等 2.由于每次对string进行索引时,都要构建proxychar的临时对象,存在额外的开销 3.在隐式类型转换上,proxy类即使声明了自己的隐式类型转换函数,将proxy转换为其代理的类型,仍然存在很多类型转换的问题,为了避免后续的类型转换错误,一般的做法是直接将构造函数直接声明为explicit。
class TVStation { public: TVStation(int channel); //... }; void watchTV(const TVStation& station, float hoursToWatch); watchTV(10, 2.5); // 借助于 int 到 TVStation 的隐式类型转换 Array<int> intArray; intArray[4] = 10; watchTV(intArray[4], 2.5);//失败,不可以通过int的代理类转换为TVStation类