More Effective C++读书笔记:条款30

Aki 发布于 2022-10-23 230 次阅读


30 Proxy class(替身类,代理类):

1. 所谓代理类(proxy class),指的是"它的每一个对象都是为了其他对象而存在的,就像是其他对象的代理人一般".某些情况下用代理类取代某些内置类型可以实现独特的功能,因为可以为代理类定义成员函数而但却无法对内置类型定义操作.条款5就展示了一个使用代理类阻止隐式类型转换的例子.

2. 实现二维数组.

    C++没有提供分配动态二维数组的语法,因此常常需要定义一些类(模板实现这些功能),像这样:

template<class T>
class Array2D 
{
public:
    Array2D(int dim1, int dim2);
    ...
};

    既然是二维数组,那么有必要提供使用"[][]"访问元素的操作,然而[][]并不是一个操作符,C++也就不允许重载一个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];

  data[3][6]实际上进行了两次函数调用:第一次调用Array2D的operator[],返回Array1D对象,第二次调用Array1D的operator[],返回指定元素.

   (本例对proxy class作用的体现其实并不显著,因为可以对指针施加operator[],因此只要使Array2D的oeprator[]返回一个指针可达到同样效果,没有必要使用代理类)

3. 区分operator[]的读写动作

    条款29用String类的例子讨论了引用计数,由于当时无法判断non-const版本oeprator[]返回的字符将被用于读操作还是写操作,因此保险起见,一旦调用non-const版本operator[],便开辟一块新内存并复制数据结构到新内存.在这种策略下,因此如果operator[]返回的字符被用于读操作,那么分配新内存并复制数据结构的行为其实是不必要的,由此会带来效率损失,使用proxy class便可以做到区分non-const operator[]用于读还是写操作,像这样:

class String 
{
public:
    //代理类用于区分operator[]的读写操作
    class CharProxy 
    { // proxies for string chars
    public:
        CharProxy(String& str, int index); // creation
        CharProxy& operator=(const CharProxy& rhs); // lvalue
        CharProxy& operator=(char c); // uses
        operator char() const; 
    private:
        String& theString; //用于操作String,并在适当时机开辟新内存并复制
        int charIndex;
    };
    const CharProxy operator[](int index) const; // for const Strings
    CharProxy operator[](int index); // for non-const Strings
    ...
    friend class CharProxy;
private:
    RCPtr<StringValue> value;//见条款29
};

    对String调用operator[]将返回CharProxy对象,CharProxy通过重载oeprator char模拟char类型的行为,但它比char类型更有优势——可以为CharProxy定义新的操作,这样当对CharProxy使用operator=时,便可以得知对CharProxy进行写操作,由于CHarProxy保存了父对象String的一个引用,便可以在现在执行开辟内存并复制数据结构的行为,像这样:

剩余文章在这里。。。。。。