条款30: Proxy Classes(替身类/代理类)
相信么?C++缺乏对多维数组的有力支持.当我申明一个多维数组char strArray[10][10][10],仅仅意味着在栈上分配了1000Bytes的内存.对C++来说,可以理解这是一维数组.那么下面这就不好玩了.
int i = 10;
char strArray = new char[i][i][i];
不要希望C++支持这样的语法,对,它不能.所以对二维数组,我们要付出我们自己的努力.用Proxy实现二维数组,正是体现Proxy的优势。
区分operator[]的读写动作
class
RCObject
{
public
:
RCObject
():
refCount
(0),
shareable
(
true
){}
RCObject
(
const
RCObject
&):
refCount
(0),
shareable
(
true
){}
RCObject
&
operator
=(
const
RCObject
&
rhs
){
return
*
this
;}
virtual
~
RCObject
()=0;
void
AddReference
(){++
refCount
;}
voidRemoveReference(){if (--refCount == 0) deletethis;}
void
markUnshareable
(){
shareable
=
false
;}
bool
isShareable
()
const
{
return
shareable
;}
bool
isShared
()
const
{
return
refCount
> 1;}
private
:
int
refCount
;
bool
shareable
;
};
RCObject
::~
RCObject
(){}
template
<
class
T
>
class
RCPtr
{
public
:
RCPtr
(
T
*
realPtr
= 0):
pointee
(
realPtr
){
init
();}
RCPtr
(
const
RCPtr
&
rhs
):
pointee
(
rhs
.
pointee
){
init
();}
~
RCPtr
(){
if
(
pointee
)
pointee
->
RemoveReference
();}
RCPtr
&
operator
= (
const
RCPtr
&
rhs
)
{
if
(
pointee
!=
rhs
.
pointee
)
{
if
(
pointee
)
pointee
->
RemoveReference
();
pointee
=
rhs
.
pointee
;
init
();
}
return
*
this
;
}
T
*
operator
->()
const
{
return
pointee
;}
T
&
operator
*()
const
{
return
*
pointee
;}
private
:
T
*
pointee
;
void
init
()
{
if
(
pointee
== 0)
return
;
if
(
pointee
->
isShareable
() ==
false
)
pointee
=
new
T
(*
pointee
);
pointee
->
AddReference
();
}
};
class
String
{
public
:
String
(
const
char
*
value
=
""
):
value
(
new
StringValue
(
value
)){}
class
CharProxy
{
public
:
CharProxy
(
String
&
str
,
int
index
):
theString
(
str
),
charIndex
(
index
){}
CharProxy
&
operator
=(
const
CharProxy
&
rhs
)
{
if
(
theString
.
value
->
isShared
())
theString
.
value
=
new
StringValue
(
theString
.
value
->
data
);
theString
.
value
->
data
[
charIndex
] =
rhs
.
theString
.
value
->
data
[
rhs
.
charIndex
];
return
*
this
;
}
CharProxy
&
operator
=(
char
c
)
{
if
(
theString
.
value
->
isShared
())
theString
.
value
=
new
StringValue
(
theString
.
value
->
data
);
theString
.
value
->
data
[
charIndex
] =
c
;
return
*
this
;
}
operator
char
()
const
{
return
theString
.
value
->
data
[
charIndex
];
}
protected
:
private
:
String
&
theString
;
int
charIndex
;
};
const
CharProxy
operator
[](
int
index
)
const
{
return
CharProxy
(
const_cast
<
String
&>(*
this
),
index
);
}
CharProxy
operator
[](
int
index
)
{
return
CharProxy
(*
this
,
index
);
}
friend
class
CharProxy
;
protected
:
private
:
struct
StringValue
:
public
RCObject
{
char
*
data
;
StringValue
(
const
char
*
initValue
)
{
init
(
initValue
);
}
StringValue
(
const
StringValue
&
rhs
)
{
init
(
rhs
.
data
);
}
void
init
(
const
char
*
initValue
)
{
data
=
new
char
[
strlen
(
initValue
) + 1];
strcpy
(
data
,
initValue
);
}
~
StringValue
()
{
delete
[]
data
;
}
};
RCPtr
<
StringValue
>
value
;
};
这是一个非常有意思的命题,Proxy在这里做一个判定的延迟,由于应用了Proxy(),这里有了对读写的判断标准。对于所有的operator[]都记录了index,返回一个CharProxy,那么下面就可以做文章了,如果只是需要读,也就是说返回一个char,那么使用char()的隐式转换就可以了,但对于写操作那肯定需要调用operator=,就可以据此来判断读写,从来决定String是否需要重新生成实值。
Proxy可以完成很多的工作,比如多维数组的实现,左值/右值的区分,压抑隐式性别转换。但Proxy也不是万能的,就如同Smart Pointer永远不是原生指针,Proxy对象永远不是基本对象,所有不要期待Proxy对象能完成基本对象所有的功能。在Proxy对象实现的过程当中,事实上是有很多工作要做的。这带来很多其他的问题,比如Debug上的困难。当两者差异不是关键的时候,Proxy能带来极大的好处。这真是一把双刃剑。我的原则是除非好处多到无法拒绝,否则用最简单的办法写代码。