В чем разница между объявлением конструктора private и =delete?

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

Оба из следующих двух не позволяют использовать конструктор копирования:

1.

class Track
{
public:
  Track(){};
  ~Track(){};
private:
  Track(const Track&){};
};

2.

class Track
{
public:
  Track(){};
  ~Track(){};
  Track(const Track&)=delete;
};

Является ли один из этих способов "более правильным", чем другой, или равны? Есть ли побочный эффект?

//Does not compile with both the above ways
int main()
{
  Track l;
  Track p(l);
}

7 ответов

Решение

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

= delete удаляет конструктор Он не генерируется компилятором и просто не будет существовать.

Так что, скорее всего, = delete это то, что вы хотите. (хотя с оговоркой, что не все компиляторы поддерживают этот синтаксис, так что если переносимость является проблемой...)

Объявление конструктора копирования private по-прежнему позволяет функции-члены Track класс для копирования-создания экземпляров этого класса, в то время как его удаление просто запрещает копирование этого объекта.

В C++11 удаление конструктора копирования - это правильный способ выразить тот факт, что класс не подлежит копированию (если, конечно, для вас не имеет смысла разрешать функции-члены Trackили друзья Trackскопировать-построить Track объекты).

Создание частного конструктора было в основном "взломом" в старом C++, поскольку это был единственный способ запретить пользователям использовать их. Способность к delete специальные функции-члены были введены только в C++11, и это лучший и более идиоматический способ сказать, что класс не может быть скопирован. так как это явно о намерении.

Частные конструкторы имеют другое использование, кроме полного запрещения их использования (например, они могут вызываться статическими функциями-членами класса). Так что простое создание конструктора не очень хорошо передает намерение, и получающаяся ошибка также не очень ясна.

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

Проблема с этим, однако, заключается в том, что намерение не может быть очевидным. Кто-то, читающий код, может не понять, почему существует осиротевшее объявление, и может по ошибке удалить его. Комментарии могут помочь, как бы унаследовав от boost::noncopyable если Boost доступен для вас.

Второй подход делает намерение очевидным и это то, что вы должны предпочесть, если вы можете использовать C++11.

Ваше первое решение сообщает читателю, что конструктор копирования является частным и не должен использоваться. Ваше второе решение действительно только в C++11. Из-за этого я бы сказал, что более портативная и удобочитаемая реализация будет вашей первой, используя private-свойство.

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

Во втором случае синтаксис запрещает копирование. Это родной C++.

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

Вы можете просто использовать "= delete" и четко указать, что вы пытаетесь сделать.

Если вы на C++11, используйте delete, Причина в том, что он делает вызов явным, а цель - ясной. Вы все еще можете случайно использовать приватный конструктор (например, в ограниченном наборе областей), но компилятор запретит вам использовать удаленный конструктор.

Одной из проблем приватного конструктора является то, что класс и его друзья все еще могут его использовать - это приводит не к ошибкам доступа, а к ошибкам связи, которые трудно отследить до места вызова.

Если ваши необходимые цепочки инструментов не поддерживают удаленные конструкторы (= delete), вы не должны определять его (как видно из вашего вопроса) - только объявите его и оставьте его неопределенным, например: private: \n Track(const Track&);

Другие вопросы по тегам