2015-03-10 1 views
4

Я начинаю новый проект на C++ 11 и только что узнал о ключевом слове delete, который позволяет предотвратить случайный вызов конструкторов копирования и т. Д. Есть ли «рекомендуемый» набор исключений, который я могу сделать во всем мире для повышения безопасности типов, например, предотвращение подписывания без знака в выражениях? Должен ли я по умолчанию использовать delete все 5 операций, которые я могу delete на всех моих классах?В новых проектах на C++ 11 я должен удалить все операции по умолчанию?

FYI, эта программа требует высокой производительности (именно поэтому я использую C++, впервые за многие годы), и есть очень немного раз, я хочу что-то копировать, поэтому копия обычно является ошибкой, хотя не 100 % времени, поэтому меня интересует этот конкретный случай, а также общий случай для других программ. Я мог бы потенциально создать конструктор копирования и добавить отдельный метод, который копирует объект в редкое время, когда мне нужна копия. Будет ли это хорошей идеей?

+2

Это действительно зависит от ваших прецедентов. Вы хотите запретить копирование? Затем пометьте экземпляр-конструктор (и оператор присваивания копии) как удаленный. Не делайте этого по обычной общей основе, делайте это только при необходимости, это мое личное мнение *. –

+5

Вы уверены, что копируете, если проблема с производительностью? Такое мышление часто приводит к менее эффективному коду. – juanchopanza

+0

Что вы подразумеваете под исключениями, которые вы хотите сделать «глобально»? –

ответ

7

Есть ли «рекомендуемый» набор исключений, которые я могу сделать во всем мире для повышения безопасности типа, например, предотвращение подписывания без знака в выражениях?

Нет. Вы, конечно же, не можете предотвратить целые преобразования, удалив что-либо.

Должен ли я по умолчанию удалять все 5 операций, которые можно удалить во всех моих классах?

Нет! Удаленный деструктор сделает невозможным уничтожить что угодно!

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

Вы, кажется, попадаете в ловушку, видя, что функция доступна, и думайте, что вы должны ее использовать. Следующая цитата из The Old Man and the C кажется уместным:

Один последний анекдот о ранее историю , где кто-то использовал INT ссылочный параметр. В , обсуждая эту статью, комментарий программиста был - «Ну, функция была на языке, поэтому я решил, что я должен использовать ее». Мы считаем, что это не достаточные критерии для использования функции C++. Функцию следует использовать только тогда, когда можно продемонстрировать, что она является . Гора поднимается «потому что она есть». То же самое не должно выполняться для функций C++. Их простое существование не является оправданием для использования.

Что касается окончательного вопроса:

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

... возможно ... это может быть случай, когда удаленный конструктор копирования может быть продемонстрирована на пользу. Но я бы сказал, что вообще нет, не делайте этого, потому что это затрудняет хранение объекта в контейнерах, например. Лучше всего просто писать свой код, чтобы избежать нежелательных копий. Если вы не сделали этого и продемонстрировали, что действительно делаете случайные копии, возможно, передумайте удаление конструктора копирования.

Но вы не должны удалять операции «по умолчанию» только потому, что узнали об этой функции. Это должно быть более распространено для использования = default, а не для = delete, за исключением классов, которые моделируют некоторые атипичные поведения, такие как уникальное право собственности на ресурс.

+1

Ну, 'void f (int); template void f (T) = delete; '. Не допускается неявное преобразование, потому что шаблон будет лучше соответствовать. –

+0

@ T.C. true, но это не поможет в выражениях _ в соответствии с запросом. Вы могли бы написать целый набор таких функций и перенести каждую операцию ... но в этот момент вы могли бы просто использовать другой язык без стольких небезопасных неявных преобразований :) –

5

В моем опыте есть определенные случаи, когда удаление определенных конструкторов или операторов имеет смысл.

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

Для предотвращения нежелательного копирования назначений, следующий по умолчанию включен в unique_ptr класса:

unique_ptr& operator= (const unique_ptr&) = delete; 

Это один экземпляр, когда он будет иметь смысл, чтобы удалить оператор из класса. Я бы сказал, что это очень суждение. Мое общее правило заключается в том, чтобы спросить себя: «Нет ли абсолютно никакого сценария, в котором клиент класса должен использовать x-оператор или y-конструктор?» Если вы затем решите, что x или y на самом деле никогда (или, возможно, только редко) не будут использоваться, то, на мой взгляд, может иметь смысл идти вперед и удалять указанного оператора/конструктора.

+1

+1, и следствие вашего заключительного абзаца состоит в том, что вы должны ** не ** удалять операции "по умолчанию", доказано, что это полезно. –

1

Да, эта идея кажется хорошей. Объявление всех нежелательных специальных методов с помощью delete отключит их, как и вы.

Однако это было возможно до того, как было изобретено delete - boost::noncopyable был использован для этой цели до выпуска C++ 11. Даже с C++ 11 наследование от boost::noncopyable кажется более простым (менее типичным), чем маркировка всех нежелательных специальных элементов с помощью = delete.

В дополнение, поскольку вы упомянули «предотвращение подписки на неподписанное», вы должны знать, что преобразования между встроенными типами (например, int - unsigned) являются встроенными и не могут быть отключены. Правила конверсий, которые не включают определяемый пользователем тип, одинаковы в C и C++ (с небольшим количеством исключений) и не могут быть изменены. Лучшее, что вы можете сделать, это настроить ваш компилятор на «испускать все предупреждения» и, возможно, «обрабатывать предупреждения как ошибки».

+1

Я бы не рекомендовал использовать boost :: noncopyable over = delete. Последнее ясно заявляет о намерении кодера, тогда как введение наследования только затуманивает вещи. Кроме того, для читателя нужно оптимизировать, поскольку код читается еще много раз, когда он написан. Поэтому меньшее количество символов может быть причиной выбора одной конструкции над другой. Если ваш компилятор поддерживает только C++ 03, я бы сделал оператор-конструктор копирования и оператор присваивания закрытым, чтобы предотвратить копирование. –

+0

@PaulOmta Синтаксис для удаления нежелательного копирования - это MyClass & operator = (MyClass) = delete; MyClass (MyClass &) = delete; '. Синтаксис для создания класса, который нельзя скопировать, - ': boost :: noncopyable' (возможно, введение множественного наследования доброкачественного характера). По моему мнению, последний * четко указывает намерение кодера и более оптимизирован для читателя, в то время как первый из них сложно разобрать в голове. Однако в глазах смотрящего; возможно, дополнительное усложнение множественного наследования может ввести в заблуждение; Я не вижу других недостатков. – anatolyg

+0

Согласовано, это действительно личное предпочтение (учитывая, что повышение доступно для вас). Думаю, мы оба рекомендуем по-другому. –