Перегрузка оператора индексации индекса C ++ [] таким образом, который позволяет получать ответы на обновления - PullRequest
25 голосов
/ 27 августа 2010

Рассмотрим задачу написания индексируемого класса, который автоматически синхронизирует его состояние с некоторым внешним хранилищем данных (например, файлом). Чтобы сделать это, класс должен быть осведомлен об изменениях в индексированном значении, которые могут произойти. К сожалению, обычный подход к перегрузке оператора [] не позволяет этого, например ...

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

Есть ли способ отличить доступ к значению от изменяемого значения?

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

Оба эти случая происходят после возврата функции. Есть ли какой-то другой подход к перегрузке оператора [], который мог бы иметь больше смысла?

Ответы [ 6 ]

15 голосов
/ 27 августа 2010

От оператора [] вы можете действительно только сказать доступ.
Даже если внешняя сущность использует бесплатную версию, это не означает, что запись будет иметь место, а не может.

Таким образом, вам нужно вернуть объект, который может обнаружить модификацию.
Лучший способ сделать это - обернуть объект классом, который переопределяет operator=.Эта обертка может затем сообщить магазину, когда объект был обновлен.Вы также хотели бы переопределить operator Type (приведение), чтобы можно было получить константную версию объекта для доступа к чтению.

Тогда мы могли бы сделать что-то вроде этого:* Более простая альтернатива:
Вместо того, чтобы предоставлять оператору [], вы предоставляете конкретный метод set для объекта хранилища и предоставляете доступ только для чтения через оператора []

10 голосов
/ 27 августа 2010

Вы можете иметь (неконстантный) оператор [], возвращающий прокси-объект, который сохраняет ссылку или указатель на контейнер и в котором оператор = сигнализирует контейнеру обновления.

(Идея использования const против неконстантного оператора [] - это красная сельдь ... вы можете знать, что вы только что отдали неконстантный доступ к объекту, но не знаете, есть ли такой доступ все еще используется для чтения или записи, когда эта запись завершается, или после этого у них есть какой-либо механизм для обновления контейнера.)

5 голосов
/ 24 февраля 2014

Еще одно элегантное (ИМХО) решение ... На самом деле это основано на том факте, что перегрузка 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

4 голосов
/ 27 августа 2010

Возвращает прокси-объект, который будет иметь:

  • operator = (Type const &), перегруженный для записи
  • operator Type () для чтения
1 голос
/ 27 августа 2010

в приведенном вами примере доступа вы можете получить различие, используя версию const:

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

для sidenote, использование size_t в качестве индекса избавляет от необходимости проверки, если index> = 0

0 голосов
/ 14 декабря 2016
    #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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...