Подтвердить что ты не робот

Должен ли конструктор/оператор/функция копирования указать, какой вариант копирования он реализует?

Вчера я спросил вопрос о копировании объектов на С#, и большинство ответов было сосредоточено на различии между глубокой копией и мелкой copy, и тот факт, что должно быть ясно, какой из обоих вариантов копирования реализует данный конструктор копирования (или оператор или функция). Я нахожу это странным.

Я написал много программного обеспечения на С++, язык, который сильно зависит от копирования, и мне никогда не требовалось несколько вариантов копирования. Единственная операция копирования, которую я когда-либо использовал, - это тот, который я называю " достаточно глубокой копией". Он выполняет следующие действия:

  • Если объект имеет право собственности на переменную-член (cf. composition), он копируется рекурсивно.
  • Если объект не имеет права собственности на переменную-член (см. aggregation), копируется только ссылка.

Теперь мой вопрос трижды:

  • 1) Требуется ли когда-нибудь объект более одного варианта копирования?
  • 2) Требуется ли функция копирования для определения того, какой вариант копирования он реализует?
  • 3) В стороне, есть ли лучший термин для того, что я называю "достаточно глубокой копией"? Я задал вопрос об определении термина "глубокая копия" .
4b9b3361

Ответ 1

Объекту нужно только скопировать то, что ему нужно скопировать. Хотя этот вопрос отмечен как язык агностик, и вы упомянули С++, я предпочитаю объяснять в терминах С# (так как это то, что я больше всего знаю). Однако концепции схожи.

Типы значений похожи на структуры. Они живут непосредственно в экземпляре объекта. Поэтому при копировании объекта у вас нет выбора, кроме как скопировать тип значения. Таким образом, вам вообще не нужно беспокоиться об этом.

Ссылочные типы похожи на указатели, и это становится сложной задачей. В зависимости от типа ссылочного типа вы можете или не хотите иметь глубокую копию. Общее правило состоит в том, что если ссылочный тип (как член объекта) зависит от состояния внешнего объекта, он должен быть клонирован. Если нет, и никогда не будет, это не обязательно.

Другим способом мышления является то, что объект, переданный вашему объекту снаружи, вероятно, НЕ должен быть клонирован. Объект, сгенерированный вашим классом, должен быть.

Хорошо, я соврал, я буду использовать некоторый С++, потому что это лучше всего объяснит, что я имею в виду.

class MyClass {
    int foo;
    char * bar;
    char * baz;

public: MyClass(int f, char * str) {
        this->foo = f;
        bar = new char[f];
        this->baz = str;
    }
};

С помощью этого объекта есть два строковых буфера, с которыми нужно иметь дело. Первый, bar, создается и управляется самим классом. Когда вы клонируете объект, вы должны выделить новый буфер.

baz, с другой стороны, не должно быть. На самом деле вы не можете, так как у вас недостаточно информации для этого. Указатель следует просто скопировать.

И, конечно, foo - это просто число. Просто скопируйте его, больше не о чем беспокоиться:)

В заключение, чтобы ответить на ваши вопросы напрямую:

  • 99% времени, нет. Там только один способ скопировать это имеет смысл. Однако этот способ меняется.
  • Не напрямую. Документирование это хорошая идея, но что-то внутреннее должно оставаться внутренним.
  • Просто "Deep copy". Вы должны (Edit: ALMOST) никогда не пытаться клонировать объект или указатель, который вы не контролируете, чтобы освободить от правил:)

Ответ 2

Различие между "глубокой копией" и "мелкой копией" имеет смысл в качестве детали реализации, но позволяет ей просачиваться дальше, что обычно указывает на недостаток абстракции, который, скорее всего, проявится и другими способами.

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

Если объект Foo содержит ссылку на объект исключительно для инкапсуляции изменяемых и неизменных аспектов объекта , отличного от идентификатора, но ссылка на этот объект никогда не будет подвергаться действию чего-либо, что будет мутировать его, применяется такая же ситуация.

Если объект Foo содержит ссылку на объект исключительно для целей инкапсуляции изменяемых и неизменных аспектов объекта , отличного от идентификатора, и объект, о котором идет речь, будет мутирован, тогда правильная копия Foo должна содержать ссылку на дубликат инкапсулированного объекта.

Если объект Foo содержит ссылку на объект исключительно для инкапсуляции неизменных аспектов объекта , включая идентификатор, то правильная копия Foo должна содержать дубликат ссылка; он должен NOT содержать ссылку на дублированный объект.

Если объект Foo содержит ссылку на объект для инкапсуляции как изменчивого состояния, так и объекта, то невозможно создать правильную копию Foo по отдельности. Правильная копия Foo может быть создана только путем дублирования всего набора объектов, к которым он прикреплен.

Единственный раз, когда имеет смысл говорить о "мелкой копии", это когда неполная операция используется как один из шагов при создании правильной копии. В противном случае существует только одна правильная копия "глубина", контролируемая типом состояния, инкапсулированным в ссылках на объекты.

Ответ 3

Большинство программистов на C++ не используют термины "мелкая копия" и "глубокая копия" по очень хорошей причине, что обычно существует только один способ копирования объекта. Это особенно верно в С++, потому что компилятор использует конструктор копирования во многих ситуациях, когда программист мог сказать, какой конструктор копирования использовать - например:

void f( std::string s );

нет способа сообщить компилятору, как скопировать строку.

Ответ 4

Немного позднего ответа, но С++ 11 у вас более или менее охвачено:

Решение, как подробно описано в этом ответе Какой тип указателя я использую, когда?, используйте разные типы указателей, чтобы выразить свое (совместное) право собственности.

Поскольку std::unique_ptr не копируется, вы будете вынуждены сделать копию данных, принадлежащих уникальному указателю. Утверждение всего в части членства в членстве, вероятно, всегда будет ясно, какую копию использовать для какого члена.