C++ 数组订阅:返回引用与代理类方法

C++ 数组订阅:返回引用与代理类方法,c++,operator-overloading,subscript-operator,C++,Operator Overloading,Subscript Operator,在搜索为模板类重载下标(“[]”)运算符的方法时,我遇到了两种不同的技术 第一个技巧: 重载操作符[]直接返回指向容器的指针,这将允许读取值和赋值。此技术的示例实现: template <class T> class X { int _size; T *container; public: X(int sz) { _size=sz; container=new T[sz](); } ~X() {

在搜索为模板类重载下标(“[]”)运算符的方法时,我遇到了两种不同的技术

第一个技巧:

重载
操作符[]
直接返回指向容器的指针,这将允许读取值和赋值。此技术的示例实现:

template <class T>
class X
{
    int _size;
    T *container;
public:
    X(int sz)
    {
        _size=sz;
        container=new T[sz]();
    }
    ~X()
    {

    }

    T& operator [](int indx)
    {
        return container[indx];
    }

};
template <class T>
class X
{
    int _size;
    T *container;
public:
    X(int sz)
    {
        _size=sz;
        container=new T[sz]();
    }
    ~X()
    {

    }

    class Proxy
    {
        int indx;
        X<T> &parent;
    public:
        Proxy(X<T> &p, int x) : parent(p),indx(x)
        {

        }
        void operator =(T assgn)
        {
            parent.container[indx]=assgn;
        }
        operator T const &()
        {
            return parent.container[indx];
        }
        friend class X<T>;//unnecessary line, I know!
    };
    Proxy operator[](int indx)
    {
        return Proxy(*this,indx);
    }

};
输出:

0
9
第二种技巧:

第二种技术涉及声明一个代理类并通过该类重载
操作符=
。此技术的示例实现:

template <class T>
class X
{
    int _size;
    T *container;
public:
    X(int sz)
    {
        _size=sz;
        container=new T[sz]();
    }
    ~X()
    {

    }

    T& operator [](int indx)
    {
        return container[indx];
    }

};
template <class T>
class X
{
    int _size;
    T *container;
public:
    X(int sz)
    {
        _size=sz;
        container=new T[sz]();
    }
    ~X()
    {

    }

    class Proxy
    {
        int indx;
        X<T> &parent;
    public:
        Proxy(X<T> &p, int x) : parent(p),indx(x)
        {

        }
        void operator =(T assgn)
        {
            parent.container[indx]=assgn;
        }
        operator T const &()
        {
            return parent.container[indx];
        }
        friend class X<T>;//unnecessary line, I know!
    };
    Proxy operator[](int indx)
    {
        return Proxy(*this,indx);
    }

};
模板
X类
{
内部尺寸;
T*集装箱;
公众:
X(内部深圳)
{
_尺寸=sz;
容器=新的T[sz]();
}
~X()
{
}
类代理
{
int indx;
X&parent;
公众:
代理(X&p,int X):母公司(p),indx(X)
{
}
void运算符=(T assgn)
{
parent.container[indx]=assgn;
}
运算符T常量&()
{
返回父容器[indx];
}
朋友X类;//不必要的线,我知道!
};
代理运算符[](int indx)
{
返回代理(*此,indx);
}
};
使用相同的
main()
我们得到相同的输出


我个人喜欢第二种方法。但是,我真的想比较这两种方法。这两种技术的主要功能区别是什么。这些方法各有哪些优点?

如果您希望公开一系列未按原样存储(需要从存储转换为存储)或无法通过引用访问的元素,则可以使用您描述的基于代理的技术。例如std::vector:它在存储器中的每个字节(每个位一个)中包含八个bool。以这种方式存储它们,不可能返回对单个此类bool的引用,因此index操作符返回一个“代理对象”,以支持对所包含bool的读写


如果您可以返回对存储对象的直接引用,那么将其包装在代理中没有真正的好处,除非您希望限制赋值(例如,仅允许容器中的正值)。

通常,当您希望返回与数据的内部存储不匹配的内容时,会使用代理。典型的例子是二维矩阵,元素存储在单个数组中。如果提供了返回行或列的运算符,则需要代理。另一个例子是臭名昭著的
std::vector
,其中数据不需要存储为
bool
块,但访问必须将
bool
返回给用户


代理可用于返回内部数据表示的段的不同“视图”。在您的示例中,似乎没有理由使用它们。

为大多数客户端使用正确的代理对象要复杂得多。。。例如,如果有人说:

tcp_peer.send_from_iterator_range(&sample[2], &sample[7+1]);
如果
sample::operator[]
返回一个临时代理,并且该代理没有仔细地替换
operator&
,那么代码会询问代理本身的地址

在不失去代理拦截对数据的读取和/或写入的能力的情况下,某些客户端使用就无法得到支持,例如在

Container::element_type& ref = container[n];
ref = 20;
…客户端代码假定容器的
操作符[]
将生成对实际元素的引用。
运算符[]
返回的任何代理必须提供一个
运算符元素类型&(
)-移交这样的引用并使其自身不起作用-或拒绝(例如,仅返回
常量
引用,返回非
常量
引用无法绑定的临时by值)并强制编辑客户端代码


因此,当您需要代理时,代理的性能可以达到95%,但当您不需要代理时,最好避免使用代理。

这里是第一种技术和第二种技术(代理
技术)之间的另一个区别。使用
auto-element=createX()[3]时//createX返回一个X
来检索X中的元素(X是一个临时对象),第一种技术为您提供一个
T
,它是
3
rd元素的副本。而
代理
将为您提供一个
代理
类型的
元素
,它实际上是指一个临时X对象。因此,由于挂起代理而导致未定义的行为。