Реализуйте Copy-CTOR с точки зрения копирования-оператора или отдельно?

StackOverflow https://stackoverflow.com/questions/4122014

  •  29-09-2019
  •  | 
  •  

Вопрос

Это не дубликат Реализация конструктора копирования с точки зрения оператора = но это более конкретный вопрос. (Или так мне нравится думать.)

вступление

Учитывая (гипотетический) класс, как это:

struct FooBar {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  NonCopyable implementation_detail; // cannot and must not be copied

  // ...
};

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

Это также не имеет смысла написать для этого функцию обмена, потому что функция свопа может просто воспроизвести то, что делает STD :: SWAP (минус некомпируемый).

Поэтому, если мы хотим скопировать эти объекты, мы сами реализуем копию и копию-оператор. Это тривиально сделано, просто назначив другим членам.

Вопрос

Если нам нужно реализовать копию CTOR и оператора, следует ли реализовать COPY CTOR с точки зрения оператора копирования или мы должны «дублировать» код с списком инициализации?

То есть дано:

FooBar& operator=(FooBar const& rhs) {
  // no self assignment check necessary
  id = rhs.id;
  valX = rhs.valX;
  valZ = rhs.valZ;
  valN = rhs.valN;
  flag = rhs.flag;
  // don't copy implementation_detail
  return *this;
}

Должны ли мы написать а)

FooBar(FooBar const& rhs) {
  *this = rhs;
}

или б)

FooBar(FooBar const& rhs)
: id(rhs.id)
, valX(rhs.valX)
, valZ(rhs.valZ)
, valN(rhs.valN)
, flag(rhs.flag)
// don't copy implementation_detail
{ }

Возможными аспектами ответа будет производительность против обслуживаемости против читаемости.

Это было полезно?

Решение

В общем, я предпочитаю b) из -за а), поскольку он явно избегает каких -либо строительства членов по умолчанию. Для INT, удвоения и т. Д. Это не соображение, но это может быть для участников с дорогими операциями или побочными эффектами. Это более обслуживается, если вам не нужно рассматривать эту потенциальную стоимость/проблему, поскольку вы добавляете и удаляете участников. Списки инициализации также поддерживают ссылки и не декоративные элементы.

В качестве альтернативы, вы можете иметь подструктуру для участников не «детализации», и позволить компилятору генерировать код копирования в соответствии с линиями:

struct X 
{
    struct I
    {
        int x_;
        int y_;
    } i_;
    int z_;

    X() { }

    X(const X& rhs)
      : i_(rhs.i_), z_(0) // implementation not copied
    { }

    X& operator=(const X& rhs)
    {
        i_ = rhs.i_;
        return *this;
    } 
};

Другие советы

Обычно вы реализуете оператор назначения с точки зрения конструктора копирования (версия @roger Pate):

FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; }
friend void swap(FooBar &a, FooBar &b) {/*...*/}

Это требует предоставления swap функция, которая меняет соответствующие члены (все, кроме implementation_detail в твоем случае).

Если swap Не бросает этот подход гарантирует, что объект не остался в каком -то непоследовательном состоянии (с назначенными только членами части).

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

Если вы действительно беспокоитесь о копировании STD :: SWAP, почему бы не поместить все, кроме деталей реализации в структуру?

struct FooBarCore {
  long id;
  double valX;
  double valZ;
  long   valN;
  bool   flag; 
  // ...
};

struct FooBar {
  FooBarCore core_;
  NonCopyable implementation_detail; // cannot and must not be copied
};

Затем вы можете использовать STD :: SWAP для этой структуры в вашей функции копирования для FooBar.

FooBar& operator=(const FooBar &src) {
  FooBarCore temp(src.core_)
  swap(temp,*this.core_);
  return *this;
}

Хорошо, еще одна попытка, основываясь на моем комментарии к Этот ответ.

Оберните реализацию_detail в классифицируемый класс:

class ImplementationDetail
{
public:
  ImplementationDetail() {}
  ImplementationDetail(const ImplementationDetail&) {}
  ImplementationDetail& operator=(const ImplementationDetail&) {}

public: // To make the example short
  Uncopyable implementation_detail;
};

и используйте этот класс в своем фубаре. Сгенерированный по умолчанию конструктор копии и оператор назначения копирования для FOOBAR будет работать правильно.

Может быть implementation_detail.implementation_detail по всему вашему коду. Или, если вы управляете кодом в классе реализации_detail, просто добавьте пустой конструктор копирования и пустое оператор назначения.

Если конструктор копирования не нуждается в копировании реализации_detail и по -прежнему будет правильным (я сомневаюсь в последнем, но давайте предположим это на данный момент), реализация_дэтланга является излишним.

Таким образом, решение, похоже, состоит в том, чтобы: Сделайте realation_detail Static и полагайтесь на сгенерированный конструктор копирования по умолчанию и оператор назначения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top