В чем разница между объявлением конструктора 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&);