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

Макросы запрещают копирование и назначение класса. Google -vs-Qt

Чтобы запретить копирование или назначение класса, общепринятой практикой является создание конструктора копирования и оператор присваивания частный. У Google и Qt есть макросы, чтобы сделать это простым и понятным. Эти макросы:

Google:

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);   \
  void operator=(const TypeName&) 

Qt:

#define Q_DISABLE_COPY(Class) \
  Class(const Class &); \     
  Class &operator=(const Class &);

Вопросы: Почему сигнатуры двух операторов присваивания различны? Кажется, что версия Qt верна. Какова практическая разница между ними?

4b9b3361

Ответ 1

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

И так как вся точка в этих макросах состоит в том, что функции никогда не будут вызваны, не имеет значения, что один возвращает void.

Ответ 2

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

boost::noncopyable - пример реализации. Он используется следующим образом:

class A : noncopyable
{
    ...
};

Ответ 3

См. Boost.Utility, в частности boost:: noncopyable. Это не макрос, а базовый класс с частной копией и присваиванием. Это не позволяет компилятору генерировать неявную копию и назначение в производных классах.

edit: Извините, это не был ответ на исходный вопрос. Кстати, boost:: noncopyable использует ссылку const в качестве возвращаемого типа для оператора присваивания. У меня создалось впечатление, что тип возвращаемого значения не имеет значения, поскольку он не должен использоваться. Тем не менее, включение частного оператора не предотвращает использование внутри класса или друзей, в этом случае нестандартный тип возвращаемого значения (например, void, ссылка на константу и т.д.) Может привести к ошибкам компиляции и уловить дополнительные ошибки.

Ответ 4

Нет никакой практической разницы. Подписи оператора присваивания различаются как по типу. Обычно имеет оператор присваивания, возвращающий ссылку, чтобы разрешить цепочку:

a = b = c;

но версия, возвращающая void, также является законной и будет отлично работать для случаев, когда единственной целью является просто объявить оператор private и, следовательно, запретить использование.

Ответ 5

Из стандартного предложения 12.8, раздел 9: "Заявленный пользователем оператор назначения копирования X::operator= является нестатической неклассической функцией-членом класса X с ровно одним параметром типа X, X&, const X&, volatile X& или const volatile X&." Он ничего не говорит о типе возврата, поэтому допустим любой тип возвращаемого значения.

В разделе 10 говорится: "Если определение класса явно не объявляет оператор присваивания копии, одно объявляется неявно".

Следовательно, объявление любого X::operator=(const X&) (или любого другого из указанных типов назначения) является достаточным. Ни тело, ни тип возврата не являются значимыми, если оператор никогда не будет использоваться.

Таким образом, это стилистическая разница, когда один макрос делает то, что мы ожидаем, и сохраняем несколько символов и выполняем работу таким образом, чтобы удивить некоторых людей. Я думаю, что макрос Qt лучше стилистически. Поскольку мы говорим о макросе, мы не говорим о том, что программисту нужно вводить что-нибудь лишнее, и неспособность удивить людей - это хорошо в языковой конструкции.

Ответ 6

Другие уже ответили, почему это законно иметь разные возвращаемые значения для оператора =; IMHO jalf сказал, что это лучше всего.

Однако вы можете задаться вопросом, почему Google использует другой тип возврата, и я подозреваю, что это:

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

Конечно, эта причина недействительна, учитывая, что используется макрос, но все еще старые привычки умирают.: -)

Ответ 7

Оба выполняют ту же цель

Как только вы напишете это:

Class &operator=(const Class &);

вы получите преимущества цепочки. Но в этом случае вы хотите, чтобы оператор присваивания был закрытым. так что это не имеет значения.

Ответ 8

Версия Qt обратно совместима, а google - нет.

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

Макрос Google не обладает таким свойством.

Ответ 9

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

Лично я предпочитаю идиому наличия класса, который наследуется от пустого не скопируемого базового класса (например, boost:: noncopyable, но у меня есть свой собственный, поэтому я могу использовать его в проектах, которые не имеют возможности повышения). Пустая оптимизация базового класса позаботится о том, чтобы обеспечить нулевые служебные данные, и она проста, понятна и не зависит от функциональности макросов препроцессора.

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

Ответ 10

Кстати, если у вас есть доступ к библиотекам Boost (нет?), в библиотеке Utility была noncopyable class в течение длительного времени:

class YourNonCopyableClass : boost::noncopyable {

Очистить IMHO.