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

Какое использование конструктора частной копии в С++

Почему люди определяют частный конструктор копирования?

Когда делает конструктор копирования и оператор присваивания личным хорошим дизайном?

Если в классе нет элементов, которые являются указателями или дескрипторами уникального объекта (например, имя файла), то есть ли в других случаях там, где частный конструктор копирования - хорошая идея?

Тот же вопрос применяется для оператора присваивания. Учитывая, что большинство С++ вращается вокруг копирования объектов и передачи по ссылке, есть ли хорошие проекты, которые включают частный конструктор копирования?

4b9b3361

Ответ 1

Некоторые объекты представляют собой определенные объекты, которые нельзя или не следует копировать. Например, вы можете предотвратить копирование объекта, который представляет файл журнала, используемый приложением, что соответствует ожиданию того, что один файл журнала будет использоваться всеми частями кода. Использование случайно или неправильно скопированного объекта может привести к появлению в журнале неподходящего содержимого, неточности записей текущего размера журнала, нескольких попыток (некоторые неудачные) для "катания" с новым именем файла журнала или переименования существующего.

Другое использование - принудительное копирование с помощью виртуальной функции. Поскольку конструкторы не могут быть virtual, обычной практикой является предотвращение прямого доступа к конструктору копирования и предоставление метода virtual Base* clone(), который возвращает копию фактического типа времени выполнения, на который указывает указатель. Это предотвращает случайное нарезку, которое будет Base b(derived).

Другой пример: мертвый простой объект интеллектуального указателя, который просто удаляет указатель, указанный в конструкторе: если он не поддерживает подсчет ссылок или какой-либо другой способ обращения с несколькими владельцами и не хочет иметь риск неудобным непреднамеренная std::auto_ptr передача стиля собственности, тогда просто скрытие конструктора копии дает большой маленький умный указатель, который быстр и эффективен для ограниченных случаев, когда он может использоваться. Ошибка времени компиляции, связанная с попыткой ее скопировать, спросит программиста: "эй, если ты действительно хочешь это сделать, меняй меня на общий указатель, иначе отступим!".

Ответ 2

В одном случае используется singleton pattern, где может быть только один экземпляр класса. В этом случае вам нужно сделать ваши конструкторы и оператор присваивания = private, чтобы не было способа создать несколько объектов. Единственный способ создать объект - через функцию GetInstance(), как показано ниже.

// An example of singleton pattern
class CMySingleton
{
public:
  static CMySingleton& GetInstance()
  {
    static CMySingleton singleton;
    return singleton;
  }

// Other non-static member functions
private:
  CMySingleton() {}                                  // Private constructor
  ~CMySingleton() {}
  CMySingleton(const CMySingleton&);                 // Prevent copy-construction
  CMySingleton& operator=(const CMySingleton&);      // Prevent assignment
};

int main(int argc, char* argv[])
{
  // create a single instance of the class
  CMySingleton &object = CMySingleton::GetInstance();

  // compile fail due to private constructor
  CMySingleton object1;
  // compile fail due to private copy constructor
  CMySingleton object2(object);
  // compile fail due to private assignment operator
  object1 = object;

  // ..
  return 0;
}

Ответ 3

Очень плохой пример:

class Vehicle : { int wheels; Vehicle(int w) : wheels(w) {} }

class Car : public Vehicle { Engine * engine; public Car(Engine * e) : Vehicle(4), engine(e) }

...

Car c(new Engine());

Car c2(c); // Now both cars share the same engine!

Vehicle v;
v = c; // This doesn't even make any sense; all you have is a Vehicle with 4 wheels but no engine.

Что значит "копировать" автомобиль? (Является ли автомобиль автомобильной моделью или экземпляром автомобиля? Копирует ли это сохранение регистрации транспортного средства?)

Что означает назначение транспортного средства другому?

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

Ответ 4

Общей причиной того, чтобы сделать конструктор копирования и назначение копии частным, является запрет выполнения этих операций по умолчанию. Однако в С++ 0x для этой цели есть специальный синтаксис = delete. Таким образом, в С++ 0x создание копии ctor private похоже на очень экзотические случаи.

Копирование ctors и присваиваний - довольно синтаксический сахар; поэтому такой "частный сахар" кажется симптомом жадности:)

Ответ 5

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

Ответ 6

Возможно, вы захотите реализовать некоторые из методов класса с помощью конструктора копирования, но не выставлять его вне класса. Итак, вы делаете это частным. Как и любой другой метод.

Ответ 7

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

Заметим:

class Base {

public:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = new Derived(*obj);

Приведенный выше код выдаст результат:

"Base copy constructor"

Это явно не то поведение, которое хотел программист! Программист пытался скопировать объект типа "Производный", но вместо этого вернулся объект типа "База"!!

Проблема устранена с использованием вышеупомянутой идиомы. Соблюдайте приведенный выше пример, переписанный для использования этой идиомы:

class Base {

public:
  virtual Base * clone () const = 0; //this will need to be implemented by derived class

protected:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
  virtual Base * clone () const {

    //call private copy constructor of class "Derived"
    return static_cast<Base *>( new Derived(*this) );
  }

//private copy constructor:
private:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = obj->clone();

Приведенный выше код выдаст результат:

"Base copy constructor"
"Derived copy constructor"

Другими словами, объект, который был построен в желаемом типе "Производный", а не типа "Base"!

Как вы можете видеть, в Derived тип конструктор копирования был намеренно закрыт, потому что это было бы плохой дизайн API, чтобы дать программистам возможность случайно попытаться вызвать конструктор копирования вручную, а не использовать умный интерфейс by clone(). Другими словами, доступный публично доступный конструктор публичных копий может заставить программистов совершить ошибку, упомянутую в части 1. В этом случае наилучшая практика заключалась бы в том, что конструктор копирования скрыт от представления и доступен только косвенно с помощью метода "clone ( )".