在我们的带引用计数的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类