Рассмотрим задачу написания индексируемого класса, который автоматически синхронизирует его состояние с некоторым внешним хранилищем данных (например, файлом). Чтобы сделать это, класс должен быть осведомлен об изменениях в индексированном значении, которые могут произойти. К сожалению, обычный подход к перегрузке оператора [] не позволяет этого, например ...
Type& operator[](int index)
{
assert(index >=0 && index < size);
return state[index];
}
Есть ли способ различить доступ к значению и изменяемое значение?
Type a = myIndexable[2]; //Access
myIndexable[3] = a; //Modification
Оба эти случая происходят после того, как функция вернулась. Есть ли какой-то другой подход к перегрузке оператора [], который мог бы иметь больше смысла?
От оператора[] можно узнать только доступ.
Даже если внешний объект использует бесплатную версию, это не означает, что запись будет иметь место, а может быть.
Таким образом, вам нужно вернуть объект, который может обнаруживать изменения.
Лучший способ сделать это — обернуть объект классом, который переопределяет operator=
. Затем эта оболочка может сообщить хранилищу, когда объект был обновлен. Вы также можете переопределить Тип оператора
(приведение), чтобы можно было получить константную версию объекта для доступа на чтение.
Тогда мы могли бы сделать что-то вроде этого:
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);
}
Более простой вариант:
Вместо того, чтобы предоставлять оператор [], вы предоставляете конкретный метод набора для объекта хранилища и предоставляете доступ для чтения только через оператор []
Вы можете сделать так, чтобы (неконстантный) оператор[] возвращал прокси-объект, который хранит ссылку или указатель на контейнер, и в котором оператор = сообщает контейнеру об обновлении.
(Идея использования константного и неконстантного оператора[] является отвлекающим маневром... вы можете знать, что вы только что предоставили неконстантный доступ к объекту, но вы не знаете, будет ли этот доступ все еще используется для чтения или записи, когда эта запись завершается, или иметь какой-либо механизм для обновления контейнера после этого.)
Вернуть прокси-объект, который будет иметь:
в приведенном вами примере доступа вы можете получить различие, используя версию const:
const Type& operator [] ( int index ) const;
на боковой заметке, используя size_t в качестве индекса, избавляет от необходимости проверять, если индекс> = 0