Рассмотрим следующий код:
// A non-copyable, non-movable aggregate
struct Strange
{
const int & i;
char & c;
};
class Container
{
private:
int my_i;
char my_c;
Strange thing;
public:
// Valid, because both `my_i´ and `my_c´ are non-const
// objects to which both references can be bound.
explicit
Container
( )
noexcept
: thing{ my_i , my_c }
{ }
// How could this be implemented?
auto &
operator=
( const Container & that )
noexcept
{
this->my_i = that->my_i;
this->my_c = that->my_c;
// What to do with `thing´?
return *this;
}
};
Возможные решения
Динамическое размещение объекта
Strange
class Container { private: int my_i; char my_c; Strange * thing; public: // Note that it isn't exception safe. explicit Container ( ) : thing(new Strange{ my_i , my_c }) { } auto & operator= ( const Container & that ) noexcept { this->my_i = that->my_i; this->my_c = that->my_c; delete this->thing; this->thing = new Strange { this->my_i , this->my_c }; return *this; } };
Обеспокоенность:
- Not efficient.
- Небезопасно: выделение может завершиться ошибкой и бросить вызов.
Опасно: необходимо проявлять большую осторожность, чтобы не допустить утечки памяти.
Использование интеллектуального указателя (например,
std::unique_ptr
) решит только последнюю точку, не говоря уже о том, что код станет более читаемым.
Использовать новое место размещения
class Container { private: int my_i; char my_c; Strange thing; public: explicit Container ( ) noexcept : thing{ my_i , my_c } { } auto & operator= ( const Container & that ) noexcept { this->my_i = that.my_i; this->my_c = that.my_c; // Placement new is exception safe, and so is // construction of `Strange´. this->thing.~Strange(); new(&this->thing) Strange { this->my_i , this->my_c }; return *this; } };
Обеспокоенность:
Освободит ли деструктор
Strange
память, занятуюthing
?Я думаю, что так же, как и конструкторы, деструкторы не отвечают за управление памятью. Более того, мой код работает нормально. Однако хотелось бы пояснений по этому поводу.
Как насчет выравнивания памяти?
Я предполагаю, что, поскольку он заменяет существующий объект того же типа, память уже будет выровнена. Это верно?
Позаботится ли деструктор
Container
об уничтоженииthing
?
Вопросы
Помимо подтверждения и/или опровержения опасений, которые я изложил выше, я хотел бы знать, есть ли другие альтернативы. Если да, то приведите пример реализации.
Этот вопрос возник при работе над классом, который должен предлагать интерфейс, аналогичный интерфейсу std::unordered_map
. Вместо того, чтобы переопределять его, мой класс инкапсулирует такой контейнер и просто действует как прокси для большинства методов: его итераторы охватывают итераторы, предоставляемые картой, а его пара представляет собой агрегированную структуру с членами с соответствующими именами (которые являются ссылками на фактические данные), представленные в приведенных примерах как Strange
. Поскольку итераторы должны возвращать ссылки и указатели на фактические данные, мои пользовательские итераторы содержат пару. Проблема заключалась в его изменении (при увеличении или назначении итератора). Я признаю, что это, вероятно, не очень хорошая идея, и что эти ссылки повлияют на производительность, но меня все равно интересует этот вопрос.
Редактировать
Я только что понял, что вместо того, чтобы возвращать ссылки и указатели на пользовательскую пару элементов, указывающую на фактические данные (инкапсулированную карту) из моего пользовательского итератора, я мог бы возвращать созданные на месте пользовательские пары (т.е. Strange
объекты). Часто мы не видим, что находимся в пещере, и вместо того, чтобы выйти из нее, продолжаем идти вперед :). Извините за шум, я отмечу вопрос как "Закрытый".
thing
содержит ссылки на самих себя, в опубликованном вами коде вам не нужно ничего делать с ним в операторе присваивания копии. - person T.C.   schedule 01.09.2014Container
в целом необходимы, потому что на самом деле это пользовательский итератор, обертывающийstd::unordered_map
. Поскольку я также хотел предоставить свои собственные пары с именованными элементами, итератор также должен был их инкапсулировать. Но ссылки должны быть изменены при увеличении итератора. - person Kalrish   schedule 01.09.2014