Как правило, вы должны использовать только перегрузку оператора, когда это естественно. Если вы поцарапаете подходящего оператора для некоторой функциональности, то это хороший признак того, что вы не должны заставлять перегрузку оператора по вашей проблеме.
Сказав это, вы пытаетесь создать прокси-объект, который отправляет события чтения и записи одной из пары объектов. Проксирующий объект часто перегружает оператор ->
, чтобы дать подобную указателю семантику. (Вы не можете перегрузить .
.)
В то время как вы можете иметь два перегруженных ->
дифференцированы const
-ness, я хотел бы предостеречь против этого, поскольку это является проблематичным для действий чтения. Перегрузка выбирается путем ссылки на объект через константу или неконстантную ссылку, а не на то, действительно ли действие является фактически прочитанным или записывается. Этот факт делает склонность к ошибкам.
Что вы можете сделать, это разделить доступ из хранилища и создать шаблон класса нескольких буферов и шаблон доступа к буферам, который обращается к соответствующему элементу, используя operator->
для синтаксической простоты.
Этот класс хранит несколько экземпляров параметра шаблона T
и сохраняет смещение, чтобы различные аксессоры могли извлекать передний/активный буфер или другие буферы путем относительного смещения. Использование параметра шаблона n == 1
означает, что существует только один экземпляр T
, и мульти-буферизация эффективно отключена.
template< class T, std::size_t n >
struct MultiBuffer
{
MultiBuffer() : _active_offset(0) {}
void ChangeBuffers() { ++_active_offset; }
T* GetInstance(std::size_t k) { return &_objects[ (_active_offset + k) % n ]; }
private:
T _objects[n];
std::size_t _active_offset;
};
Этот класс абстрагирует выбор буфера. Он ссылается на MultiBuffer
посредством ссылки, поэтому вы должны гарантировать, что его срок службы короче, чем MultiBuffer
, который он использует. Он имеет собственное смещение, которое добавляется к смещению MultiBuffer
, так что разные BufferAccess
могут ссылаться на разные элементы массива (например, параметр шаблона n = 0 для доступа к фронт-буфера и 1 для доступа к буферу назад).
Обратите внимание, что смещение BufferAccess
является членом, а не параметром шаблона, так что методы, которые работают с объектами BufferAccess
, не привязаны только к одному конкретному смещению или должны быть самими шаблонами. Я сделал объект count параметром шаблона, поскольку из вашего описания он скорее всего будет вариантом конфигурации, и это дает компилятору максимальную возможность для оптимизации.
template< class T, std::size_t n >
class BufferAccess
{
public:
BufferAccess(MultiBuffer< T, n >& buf, std::size_t offset)
: _buffer(buf), _offset(offset)
{
}
T* operator->() const
{
return _buffer.GetInstance(_offset);
}
private:
MultiBuffer< T, n >& _buffer;
const std::size_t _offset;
};
Собирает все вместе с тестовым классом, обратите внимание, что при перегрузке ->
можно легко назвать член тестового класса из экземпляра BufferAccess
без BufferAccess
нуждающихся каких-либо знаний о том, что членах тестового класс имеет.
Кроме того, ни одно изменение не переключается между однократной и двойной буферизацией. Тройная буферизация также тривиальна для достижения, если вы можете найти в ней необходимость.
class TestClass
{
public:
TestClass() : _n(0) {}
int get() const { return _n; }
void set(int n) { _n = n; }
private:
int _n;
};
#include <iostream>
#include <ostream>
int main()
{
const std::size_t buffers = 2;
MultiBuffer<TestClass, buffers> mbuf;
BufferAccess<TestClass, buffers> frontBuffer(mbuf, 0);
BufferAccess<TestClass, buffers> backBuffer(mbuf, 1);
std::cout << "set front to 5\n";
frontBuffer->set(5);
std::cout << "back = " << backBuffer->get() << '\n';
std::cout << "swap buffers\n";
++mbuf.offset;
std::cout << "set front to 10\n";
frontBuffer->set(10);
std::cout << "back = " << backBuffer->get() << '\n';
std::cout << "front = " << frontBuffer->get() << '\n';
return 0;
}
небольшое изменение для ясности – gtrak
Gary: Перегрузка оператора - это плохой денотационный выбор. Вы не делаете вариант принятого оператора, вы делаете что-то довольно уникальное. Вам нужен вызов функции, который отмечает уникальность решения. –
Да, более сложная версия моей первоначальной идеи заключалась в том, что я мог бы иметь оператор + оператор вызова + в соответствующем члене соответствующей структуры данных и передать аргумент. Я понимаю, что вы имеете в виду, это может запутать. Преимущество в моем сознании - двойные буферизированные данные могут стать заменой нормальных данных, если я использую это, если я применяю некоторые правила, такие как члены, могут быть только числами или агрегатами. – gtrak