以允许响应更新的方式重载C ++索引下标operator []

时间:2010-08-27 06:56:03

标签: c++ indexing operator-overloading

考虑编写可索引类的任务,该类自动将其状态与某些外部数据存储(例如文件)同步。为了做到这一点,需要让类知道可能发生的索引值的更改。不幸的是,重载operator []的常用方法不允许这样做,例如......

Type& operator[](int index)
{
    assert(index >=0 && index < size);
    return state[index];
}

我有什么办法可以区分被访问的值和被修改的值吗?

Type a = myIndexable[2]; //Access
myIndexable[3] = a;  //Modification

这两种情况都在函数返回后发生。有没有其他方法来重载operator [],这可能更有意义?

6 个答案:

答案 0 :(得分:15)

来自运营商[],您只能真正告知访问权限 即使外部实体使用非成本版本,这也不意味着将发生写入而不是可能发生。

因此,您需要做的是返回一个可以检测修改的对象 执行此操作的最佳方法是使用覆盖operator=的类来包装对象。然后,此包装器可以在更新对象时通知存储。您还希望覆盖operator Type(强制转换),以便可以检索对象的const版本以进行读取访问。

然后我们可以这样做:

class WriteCheck;
class Store
{
  public:
  Type const& operator[](int index) const
  {
    return state[index];
  } 
  WriteCheck operator[](int index);
  void stateUpdate(int index)
  {
        // Called when a particular index has been updated.
  }
  // Stuff
};

class WriteCheck
{ 
    Store&  store;
    Type&   object;
    int     index;

    public: WriteCheck(Store& s, Type& o, int i): store(s), object(o), index(i) {}

    // When assignment is done assign
    // Then inform the store.
    WriteCheck& operator=(Type const& rhs)
    {
        object = rhs;
        store.stateUpdate(index);
    }

    // Still allow the base object to be read
    // From within this wrapper.
    operator Type const&()
    {
        return object;
    }   
};      

WriteCheck Store::operator[](int index)
{   
    return WriteCheck(*this, state[index], index);
}

更简单的替代方案是:
而不是提供operator [],而是在store对象上提供特定的set方法,并且只通过operator []

提供读访问权限。

答案 1 :(得分:10)

你可以让(非const)运算符[]返回一个代理对象,该代理对象保存一个引用容器的引用或指针,以及operator =表示更新容器的信号。

(使用const vs非const运算符[]的想法是一个红色的鲱鱼......你可能知道你刚刚放弃了对该对象的非const访问,但你不知道该访问是否当写入完成时,仍然用于读取或写入,或者之后有任何更新容器的机制。)

答案 2 :(得分:5)

另一个优雅(恕我直言)的解决方案...... 实际上它是基于这样的事实:只有在const对象上使用时才调用const重载。 让我们首先创建两个[]重载 - 因为它是必需的,但使用不同的位置:

Type& operator[](int index)
{
    assert(index >=0 && index < size);
    return stateWrite[index];
}
const Type& operator[](int index) const
{
    assert(index >=0 && index < size);
    return stateRead[index];
}

现在,当您需要按如下方式“读取”对象时,应该创建对象的阴影引用:

const Indexable& myIndexableRead = myIndexable; // create the shadow
Type a = myIndexableRead[2]; //Access
myIndexable[3] = a;  //Modification

创建此影子声明实际上并不在内存中创建任何内容。它只是通过“const”访问为您的对象创建另一个名称。它全部在编译阶段解决(包括使用const重载),并且不会影响运行时的任何内容 - 无论是内存还是性能。

最重要的是 - 它比创建任何赋值代理等更优雅(恕我直言)。我必须声明语句“ 来自运算符[]你只能真正告诉访问 “不正确。根据C ++标准,通过引用返回动态分配的对象或全局变量是允许其直接修改的最终方式,包括[]重载情况。

以下代码已经过测试:

#include <iostream>

using namespace std;

class SafeIntArray {
    int* numbers;
    int size;
    static const int externalValue = 50;

public:
    SafeIntArray( unsigned int size = 20 ) {
        this->size = size;
        numbers = new int[size];
    }
    ~SafeIntArray() {
        delete[] numbers;
    }

    const int& operator[]( const unsigned int i ) const {
        if ( i < size )
            return numbers[i];
        else
            return externalValue;
    }

    int& operator[]( const unsigned int i ) {
        if ( i < size )
            return numbers[i];
        else
            return *numbers;
    }

    unsigned int getSize() { return size; }
};

int main() {

    SafeIntArray arr;
    const SafeIntArray& arr_0 = arr;
    int size = arr.getSize();

    for ( int i = 0; i <= size ; i++ )
        arr[i] = i;

    for ( int i = 0; i <= size ; i++ ) {
        cout << arr_0[i] << ' ';
    }
    cout << endl;

    return 0;
}

结果是:

  

20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50

答案 3 :(得分:4)

返回一个具有以下内容的代理对象:

  • operator =(类型const&amp;)重载用于写入
  • 运算符Type()用于读取

答案 4 :(得分:1)

在您给出的访问示例中,您可以使用const版本进行区分:

const Type& operator [] ( int index ) const;

在旁注上,使用size_t作为索引,无需检查index&gt; = 0

答案 5 :(得分:0)

    #include "stdafx.h"
    #include <iostream>

    template<typename T>
    class MyVector
    {
        T* _Elem; // a pointer to the elements
        int _Size;  // the size
    public:
        // constructor
        MyVector(int _size):_Size(_size), _Elem(new T[_size])
        {
            // Initialize the elemets
            for( int i=0; i< _size; ++i )
                _Elem[i] = 0.0;
        }
        // destructor to cleanup the mess
        ~MyVector(){ delete []_Elem; }
    public:
        // the size of MyVector
        int Size() const
        {
            return _Size;
        }
        // overload subscript operator
        T& operator[]( int i )
        {
            return _Elem[i];
        }
    };


    int _tmain(int argc, _TCHAR* argv[])
    {
        MyVector<int> vec(10);
        vec[0] =10;
        vec[1] =20;
        vec[2] =30;
        vec[3] =40;
        vec[4] =50;

        std::cout<<"Print vector Element "<<std::endl;
        for (int i = 0; i < vec.Size(); i++)
        {
            std::cout<<"Vec["<<i<<"] = "<<vec[i]<<std::endl;
        }

        return 0;
    }