При поиске методов перегрузки оператора Subscript (‘[]’) для шаблонного класса я натолкнулся на два разных метода.
Первая Техника:
Перегрузка operator []
возврат указателя на контейнер напрямую, что позволит как считывать значение, так и присваивать значение. Пример реализации этого метода:
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];
}
};
С main()
как:
X<int> sample(100);
cout<<sample[9]<<endl;
sample[9]=9;
cout<<sample[9]<<endl;
Выход:
0
9
Вторая техника:
Второй метод включает в себя объявление прокси-класса и перегрузку operator =
через этот класс. Пример реализации этого метода:
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);
}
};
С таким же main()
мы получаем тот же результат.
Мне лично нравится второй способ. Но я действительно хочу сравнить эти два метода. Каковы основные функциональные различия этих двух методов. Какие преимущества имеет каждый из этих методов?
Описанную вами технику на основе прокси-сервера можно использовать, если вы хотите представить последовательность элементов, которые не сохраняются как таковые (требующие преобразования из и в хранилище) или к которым нельзя просто получить доступ посредством ссылки. Примером является std :: vector < bool>: в каждом байте (по одному в каждом бите) хранится по восемь bool. Храня их таким образом, невозможно вернуть ссылку на один такой bool, поэтому оператор индекса вместо этого возвращает «прокси-объект» для поддержки чтения и записи содержащихся в них bool.
если ты Можно возвращать прямую ссылку на сохраненные объекты, нет никакого реального преимущества заключать его в прокси, если вы не хотите ограничить присваивание (например, разрешить только положительные значения в контейнере).
Обычно прокси используется, когда вы хотите вернуть что-то, что не соответствует внутреннему хранилищу данных. Классическим примером является 2D-матрица, элементы которой хранятся в одном массиве. Если вы предоставляете оператор для возврата строк или столбцов, вам нужны прокси. Другим примером является печально известный std::vector<bool>
где данные не нужно хранить как блок bool
, но доступ должен вернуться bool
пользователю.
Прокси могут быть использованы для возврата различных «представлений» сегментов внутреннего представления данных. В вашем примере, кажется, нет причин использовать их.
Получение прокси-объектов, подходящих для большинства клиентов, значительно сложнее … например, что если кто-то скажет:
tcp_peer.send_from_iterator_range(&sample[2], &sample[7+1]);
Если sample::operator[]
возвращает временный прокси, и этот прокси не заменяет тщательно operator&
Затем код запрашивает адреса самих прокси.
Некоторое использование клиента просто не может поддерживаться без потери способности Прокси перехватывать чтения и / или записи данных, например, в …
Container::element_type& ref = container[n];
ref = 20;
…код клиента предполагает наличие контейнера operator[]
даст ссылку на фактический элемент. Любой прокси возвращается operator[]
должен либо предоставить operator element_type&()
— передать такую ссылку и выйти из игры — или отказаться (например, только вернуть const
ссылка, возвращает временное значение, к которому не относитсяconst
ссылка не может быть связана) и принудительно вносит изменения в код клиента.
Итак, прокси на 95% лучше, когда они вам нужны, но лучше их избегать, когда вы этого не делаете.