web-dev-qa-db-ja.com

更新への応答を可能にする方法でのC ++インデックス添字演算子[]のオーバーロード

状態を一部の外部データストア(ファイルなど)と自動的に同期するインデックス可能なクラスを作成するタスクを考えてみましょう。これを行うには、発生する可能性のあるインデックス値の変更をクラスに認識させる必要があります。残念ながら、operator []をオーバーロードする通常のアプローチでは、これは許可されません。たとえば...

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

アクセスされている値と変更されている値を区別する方法はありますか?

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

これらの両方のケースは、関数が戻った後に発生します。 operator []をオーバーロードする他のアプローチはありますか?

25
DuncanACoulter

Operator []からは、本当にアクセスを伝えることしかできません。
外部エンティティが非コストバージョンを使用していても、書き込みが行われるのではなく、書き込みが行われることを意味しません。

したがって、必要なのは、変更を検出できるオブジェクトを返すことです。
これを行う最良の方法は、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);
}

より簡単な代替策は次のとおりです。
演算子を提供するのではなく、ストアオブジェクトに特定のセットメソッドを提供し、演算子を介した読み取りアクセスのみを提供します[]

15
Martin York

(非const)operator []に、コンテナへの参照またはポインタを保持するプロキシオブジェクトを返させることができます。この場合、operator =は更新のコンテナに信号を送ります。

(const演算子とnon-const演算子を使用するという考えは[]が赤字です...オブジェクトへの非constアクセスを許可したことを知っているかもしれませんが、そのアクセスがまだ行われているかどうかはわかりません。読み取りまたは書き込みに使用され、その書き込みが完了したとき、またはその後コンテナーを更新するためのメカニズムを持っている場合。)

10
Tony Delroy

別のエレガントな(IMHO)ソリューション...実際には、constオーバーロードは、constオブジェクトで使用された場合にのみ呼び出されるという事実に基づいています。最初に2つの[]オーバーロードを作成しましょう-必要に応じて、異なる場所を使用します。

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オーバーロードの使用を含む)、ランタイムの何にも影響を与えません-メモリもパフォーマンスも影響しません。

そして一番下の行-割り当てプロキシなどを作成するよりもはるかにエレガント(IMHO)です。 "演算子[]伝えるアクセスが正しくありません。 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

5
dzilbers

次のようなプロキシオブジェクトを返します。

  • operator =(Type const&)書き込み用にオーバーロード
  • 読み取り用の演算子Type()
4
Tomek

アクセス例では、constバージョンを使用して区別できます。

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

補足として、size_tをインデックスとして使用すると、インデックス> = 0かどうかを確認する必要がなくなります。

1
stijn
    #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;
    }
0
Praveer Kumar