一、实现多维数组
C++中支持多维数组的方法:产生一个class,用以表现我们有需要却被语言遗漏的对象;
//定义一个类模板如下:
template<class T>
class Array2D
{
public:
Array2D(int dim1,int dim2);
...
};
- 将
operator[]
重载,令它返回一个Array1D对象; - 对Array1D重载
operator[]
,令它返回原来二维数组中的一个元素:
template<class T>
class Array2D
{
public:
class Array1D
{
public:
T& operator[](int index);
const T& operator[](int index) const;
...
};
Array1D operator[](int index);
const Array1D operator[](int index) const;
...
};
//调用
Array2D<float> data(10,20);
...
cout << data[3][6];//ok
data[3]
获得一个Array1D对象,对该对象再施行operator[]
,获得原二维数组中(3, 6)
位置的浮点数;Array2D类的用户不需要知道Array1D类的存在。
- 凡“用来代表(象征)其他对象”的对象,常被称为proxy objects(替身对象);
- 用以表现proxy objects者,我们称为proxy classes。
二、区分operator[]
的读写动作
对于一个proxy,你只有3件事情可做:
- 产生它,本例也就是指定它代表哪一个字符串中的哪一个字符;
- 以它作为赋值动作的目标(接受端),这种情况下你是对它所代表的字符串内的字符做赋值动作。如果这么使用,proxy代表的将是“调用
operator[]
函数”的那个字符串的左值运用;- 以其他方式使用之。如果这么使用,proxy表现的是“调用
operatorp[]
函数”的那个字符串的右值运用。
//一个reference-counted String class,
//其中利用proxy class来区分operator[]的左值运用和右值运用:
class String //reference-counted strings见上一篇细节
{
public:
class CharProxy
{
public:
CharProxy(String& str,int index); //构造
CharProxy& operator = (const CharProxy& rhs); //左值运用
CharProxy& operator = (char c); //右值运用
operator char() const;
private:
String& theString; //这个proxy附属的字符串
int charIndex; //这个proxy所代表的字符串字符
};
const CharProxy operator[](int index) const; //针对const strings
CharProxy operator[](int index); //针对non-const strings
...
friend class CharProxy;
private:
RCPtr<StringValue> value;
};
//调用
String s1,s2;
...
cout << s1[5]; //合法(右值运用)
s2[5] = 'x'; //合法(左值运用)
s1[3] = s2[8]; //合法(左值、右值)
//String operator[]实现:
const String::CharProxy String::operator[](int index) const
{
return CharProxy(const_cast<String&>(*this), index);
}
String::CharProxy String::operator[](int index)
{
return CharProxy(*this, index);
}
每个函数都只是产生并返回“被请求之字符”的一个替代品。没有任何动作施加于此字符身上:我们延缓此等行为,直到知道该行为是“读取”或者“写”。
operator[]
返回的每一个proxy都会记住它所附属的字符串,以及它所在的索引位置:
String::CharProxy::CharProxy(String& str,int index)
:theString(str),charIndex(index){}
//将proxy转换为右值:只需要返回该proxy所表现的字符串副本就行:
String::CharProxy::operator char() const
{
return theString.value->data[index];
}
//CharProxy的赋值操作符:
String::CharProxy&
String::CharProxy::operator = (const CharProxy& rhs)
{
//如果本字符串与其他String对象共享一个实值
//将实值复制一份,供本字符串单独使用
if(theString.value->isShared()){
theString.value = new StringValue(theString.value->data);
}
//现在进行赋值动作:将rhs所代表的字符值
//赋予*this所代表的字符
theString.value->datap[charIndex] =
rhs.theString.value->data[rhs.charIndex];
return *this;
//第二个CharProxy赋值操作符和上述传统版本几乎雷同:
CharProxy& String::CharProxy::operator = (char c)
{
if(theString.value->isShared())
theString.value = new StringValue(theString.value->data);
theString.value->data[charIndex] = c;
return *this;
}
//将上述两个赋值操作符的重复代码抽出来放进一个私有的CharProxy成员函数
//然后让两个操作符都去调用它
三、限制隐式类型转换
难点:“对proxy取址”所获取的指针类型和“对真实对象取址”获取的指针类型不同。
解决:需要在CharProxy类内将取址操作符加以重载:
class String
{
public:
class CharProxy
{
public:
...
char* operator&();
const char* operator() const;
...
};
...
};
const char* String::CharProxy::operator() const
{
return &(theString.value->data[charIndex]);
}
char* String::CharProxy::operator&()
{
//确定“标的字符”所属的字符串实值不为任何其他任何String对象共享
if(theString.value->isShared()){
theString.value = new StringValue(theString.value->data);
}
//我们不知道clients会将本函数返回的指针保留多久,所以“目标字符”所属的字符串实值绝不可以被共享
theString.value->markUnshanreable();
return &(theString.value->data[charIndex]);
}
总结
-
Proxy类的优点:
允许我们完成某些几乎不可能完成的行为:多维数组、左值/右值的区分、压抑隐式转换就是其三。 -
Proxy类的缺点:
如果扮演函数返回值的角色,那些proxy对象将是一种临时对象,需要被产生和销毁,构造和析构带来的成本远大于proxies带来的好处,且软件复杂度和也随之增加。