избежание неявного копирования с отключенным конструктором копирования

Предположим, у вас есть класс NonCopyable

class NonCopyable
{
public:
   NonCopyable(int n){}
   ~NonCopyable(){}
   [...]

private:
   [members...]

private:
   NonCopyable( const NonCopyable& ); //disabled (no definition)
   NonCopyable& operator= ( const NonCopyable& ); //disabled (no definition)
};

Для этого класса нелогично иметь его копии, поэтому конструктор копирования и оператор присваивания отключены.

Однако, когда вам нужен вектор объектов NonCopyables:

std::vector<NonCopyable> m_V;
int n;
m_V.push_back(NonCopyable(n));

Здесь вы неявно вызываете конструктор копирования.

Меня учили решать эту проблему, используя указатели на эти объекты вместо самих объектов. Но это раздражает как в использовании, так и в производительности, потому что вам приходится динамически выделять эти объекты с помощью new()...

Мой вопрос: есть ли способ обойти это? Какое обычное решение этой проблемы?


person xcrypt    schedule 22.01.2012    source источник
comment
Кажется, ваш вопрос заключается в том, как я могу использовать некопируемый объект в ситуации, когда мне нужно скопировать этот объект?...   -  person Oliver Charlesworth    schedule 22.01.2012
comment
Серьезный вопрос, если нелогично иметь копии вне вектора, почему лучше иметь копии, если они внутри вектора? :)   -  person Joachim Isaksson    schedule 22.01.2012
comment
это не должно быть копией, это моя точка зрения. Я хочу снабдить вектор уникальным объектом, если вы меня понимаете   -  person xcrypt    schedule 22.01.2012
comment
@xcrypt: Но, конечно, это копия. Вектор содержит копию исходного объекта!   -  person Oliver Charlesworth    schedule 22.01.2012
comment
@OliCharlesworth Я не утверждаю, что это не копия. Я говорю, что это не должно быть копией, я хочу, чтобы вектор имел уникальные объекты   -  person xcrypt    schedule 22.01.2012
comment
@Oli: vector в C++03 имеет досадное и несущественное ограничение, заключающееся в том, что единственный способ получить в него объекты - это скопировать их туда. xcrypt не нуждается в копиях объектов: vector — это удобный способ управлять памятью числом объектов, зависящим от времени выполнения, но в C++03 это не так удобно, как в C++ 11.   -  person Steve Jessop    schedule 22.01.2012
comment
@xcrypt Я упомянул об этом в отдельном комментарии, но мой вопрос действительно здесь. Как NonCopyable может быть уникальным, если у него нет только одного объекта? В этом случае помещать его в вектор для меня не имеет смысла. Я понимаю, что я, вероятно, нуб здесь. Пожалуйста, уточните. Спасибо!   -  person batbrat    schedule 22.01.2012
comment
@batbrat Ему нужен только один объект, тот, что в векторе. Решением будет создание объекта на месте внутри вектора, что невозможно в С++ 03 (С++ 11 имеет семантику перемещения и emplace_back).   -  person pezcode    schedule 22.01.2012
comment
@pezcode Спасибо за разъяснение. Кажется, он хочет использовать std::vector только для управления памятью. Это оно? Это объясняет предложение использовать умные указатели вместо этого.   -  person batbrat    schedule 22.01.2012
comment
@batbrat: NonCopyable не означает, что он всегда есть только один (например, синглтон). Это просто означает, что нет двух одинаковых объектов, и поэтому нет смысла устанавливать один объект равным другому (присваивание копии) или создавать клон существующего объекта (копировать ctor).   -  person Steve Jessop    schedule 22.01.2012
comment
@SteveJessop Это то, о чем я думал изначально, однако xcrypt использовал термин «уникальный» в комментарии, что меня более чем смутило. Так что это просто для того, чтобы убедиться, что это вектор различных объектов. Большое спасибо за разъяснения.   -  person batbrat    schedule 22.01.2012
comment
@batbrat Извините, я плохо выразился. Я имел в виду уникальный как не копия. Однако это не означает, что вектор не может содержать несколько эквивалентных объектов. Единственным ограничением является то, что изготовление копий является незаконным.   -  person xcrypt    schedule 23.01.2012
comment
@xcrypt, все в порядке. Мне просто было трудно это интерпретировать. Спасибо за разъяснение!   -  person batbrat    schedule 23.01.2012


Ответы (2)


В C++11 есть решение, применимое ко многим некопируемым классам: сделать класс подвижным (вместо копируемого) и использовать emplace_back для добавления новых элементов в вектор.

Если вам нужно что-то придумать с C++03, возможно, вы сможете найти способ реализовать копирование "пустых" объектов NonCopyable (и использовать идею Лучиана об ограничении этой операции), а также найти способ реализовать swap. Затем вы можете сделать:

std::vector<NonCopyable> m_V;
int n;
m_V.push_back(NonCopyable());
NonCopyable(n).swap(m_V.back());
person Steve Jessop    schedule 22.01.2012
comment
Может быть нубский вопрос, но я всегда работал с C++03. Как я могу начать работать с C++11? - person xcrypt; 23.01.2012
comment
Проверьте документацию вашего компилятора, например, с gcc это -std=c++0x. - person Steve Jessop; 23.01.2012

Вы можете сделать vector другом класса:

class NonCopyable
{
   friend std::vector<NonCopyable>;
public:
   NonCopyable(int n){}
   ~NonCopyable(){}

private:
   NonCopyable( const NonCopyable& ) {}; 
   NonCopyable& operator= ( const NonCopyable& ) {}; 
};

или у вас может быть vector умных указателей на класс.

РЕДАКТИРОВАТЬ:

Я мог неправильно понять вопрос. Если вам не нужны копии класса (моя первоначальная догадка заключалась в том, что вы не хотите, чтобы копии были общедоступными), вам определенно следует использовать интеллектуальные указатели.

person Luchian Grigore    schedule 22.01.2012
comment
Вам также нужно определение для этих копий. - person Steve Jessop; 22.01.2012
comment
Преобразование vector в friend просто вызовет ошибки компоновщика, если на самом деле нет реализации конструктора копирования. - person sth; 22.01.2012
comment
@SteveJessop Я просто скопировал код, но да, им нужны определения. - person Luchian Grigore; 22.01.2012
comment
Если класс не копируется, это обычно происходит потому, что для него не имеет смысла копировать каким-либо образом... поэтому осмысленная реализация конструктора копирования и оператора присваивания, вероятно, будет невозможна. Таким образом, сделать vector другом будет мало толку. - person ; 22.01.2012
comment
@Fanael Я понял, что оператор не хочет, чтобы его копировали, а не то, что он не может быть скопирован. Я не могу найти пример ситуации, когда невозможно скопировать объект... - person Luchian Grigore; 22.01.2012
comment
У меня нубский вопрос. Как превращение вектора в друга предотвращает проблему конструктора копирования. Я действительно не понимаю. Если мне нужно снова просмотреть свои книги или какие-то ресурсы, укажите мне на них. Тем не менее, некоторые пояснения в дополнение к этому были бы очень кстати. Спасибо! - person batbrat; 22.01.2012
comment
@batbrat предотвратить что? Это просто означает, что vector разрешено делать копии класса. - person Luchian Grigore; 22.01.2012
comment
@LuchianGrigore: Знаете ли вы какую-нибудь разумную реализацию операции копирования, например, для scoped_ptr? - person ; 22.01.2012
comment
@batbrat, создающий векторного друга, позволит ему получить доступ к личным данным из класса. Таким образом, он может получить доступ к своему конструктору копирования и использовать его (при условии, что у него есть определение) - person xcrypt; 22.01.2012
comment
@LuchianGrigore Мой вопрос был плохо сформулирован. Если вектору разрешено делать копии класса, не нарушается ли требование уникальности? Кроме того, это единственный способ решить эту проблему за пределами С++ 11? - person batbrat; 22.01.2012
comment
@batbrat Я тоже неправильно понял вопрос (я думаю). Я бы использовал умные указатели - как я сказал в своем редактировании. - person Luchian Grigore; 22.01.2012
comment
@LuchianGrigore Я не так много знаю об интеллектуальных указателях, но разве вам не нужно выделять объекты, на которые они указывают, с помощью new ()? (явно или неявно). Потому что это решение, которого я пытаюсь избежать - person xcrypt; 22.01.2012
comment
Спасибо за разъяснение xcrypt, @LuchianGrigore. Я считаю, что для ее решения можно использовать указатели или умные указатели. Я нечасто использовал интеллектуальные указатели, но знаю, что главное преимущество — это подсчет ссылок и разгрузка управления памятью. Как это может быть важно здесь? Кроме того, я немного запутался в вопросе уникальности. Вектор не может содержать более одного объекта NonCopyable, иначе он не будет уникальным, верно? - person batbrat; 22.01.2012
comment
@batbrat Может содержать более одного объекта NonCopyable. Он может даже содержать два эквивалентных объекта NonCopyable. Однако объект NonCopyable не может быть скопирован. Что касается интеллектуальных указателей, они имеют подсчет ссылок и многое другое, но они все равно должны указывать на объект. Это означает, что либо вам придется выделить один в стеке, либо в куче. Если бы вы разместили его в куче, вы бы использовали new() или что-то подобное, но я хочу этого избежать. Однако, если вы сделаете один в стеке, когда тело функции завершится, оно выскочит из стека (вызовите его деструктор), (продолжение) - person xcrypt; 23.01.2012
comment
(продолжение) Таким образом, интеллектуальный указатель, который содержит вектор, будет указывать на несуществующий объект. Таким образом, если интеллектуальные указатели не могут предотвратить извлечение этого объекта из стека, или если они копируют этот объект (но мы можем исключить это, поскольку он не может быть скопирован), или если он не указывает на объект в куче (мы также можем исключить это). поскольку я этого не хочу), я не вижу, как умные указатели могут решить проблему. - person xcrypt; 23.01.2012
comment
@xcrypt они не могут, если вы избегаете объектов в куче. - person Luchian Grigore; 23.01.2012