Примечание. Ответы были даны в определенном порядке , но поскольку многие пользователи сортируют ответы по голосам, а не по времени, в которое они были даны, вот индекс ответов в том порядке, в котором они имеют наибольшее значение:
- Общий синтаксис перегрузки операторов в C ++
- Три основных правила перегрузки операторов в C ++
- Решение между членом и не членом
- Общие операторы для перегрузки
- Оператор присваивания
- Операторы ввода и вывода
- Оператор вызова функции
- Операторы сравнения
- Арифметические Операторы
- Подписка на массив
- Операторы для Pointer-подобных типов
- Операторы преобразования
- Перегрузка нового и удаление
(Примечание. Предполагается, что это будет вход в FAQ по C ++ в Stack Overflow . Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация в meta, с которой все это началось, будет подходящим местом для этого. Этот вопрос отслеживается в чате C ++ , где идея FAQ возникла в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)
Ответы:
Общие операторы для перегрузки
Большая часть работы операторов по перегрузке - это код котельной плиты. Это неудивительно, поскольку операторы являются просто синтаксическим сахаром, их фактическая работа может выполняться (и часто направляется) простыми функциями. Но важно, чтобы вы правильно поняли этот код. Если вы потерпите неудачу, либо код вашего оператора не скомпилируется, либо код вашего пользователя не скомпилируется, либо код вашего пользователя будет вести себя на удивление.
Оператор присваивания
Многое можно сказать о назначении. Тем не менее, большинство из них уже было сказано в известном FAQ по копированию и замене GMan , поэтому я пропущу большую часть здесь, перечисляя только идеальный оператор присваивания для справки:
Операторы Bitshift (используются для потокового ввода-вывода)
Операторы битового сдвига
<<
и>>
, хотя они все еще используются в аппаратном интерфейсе для функций манипулирования битами, которые они наследуют от C, стали более распространенными в качестве операторов ввода и вывода перегруженного потока в большинстве приложений. Для перегрузки руководства в качестве операторов манипуляции битами см. Раздел ниже, посвященный двоичным арифметическим операторам. Для реализации собственного формата и логики разбора, когда ваш объект используется с iostreams, продолжайте.Операторы потока, среди наиболее часто перегруженных операторов, являются бинарными инфиксными операторами, для которых в синтаксисе не указано никаких ограничений относительно того, должны ли они быть членами или не членами. Поскольку они изменяют свой левый аргумент (они изменяют состояние потока), они должны, согласно практическим правилам, быть реализованы как члены типа их левого операнда. Однако их левые операнды являются потоками из стандартной библиотеки, и хотя большинство операторов вывода и ввода потока, определенных стандартной библиотекой, действительно определены как члены классов потоков, при реализации операций вывода и ввода для ваших собственных типов вы не может изменить типы потоков стандартной библиотеки. Вот почему вам нужно реализовать эти операторы для ваших собственных типов как функции, не являющиеся членами. Канонические формы этих двух:
При реализации
operator>>
ручная установка состояния потока необходима только тогда, когда само чтение завершилось успешно, но результат не соответствует ожидаемому.Оператор вызова функции
Оператор вызова функции, используемый для создания объектов функций, также известных как функторы, должен быть определен как функция- член , поэтому он всегда имеет неявный
this
аргумент функций-членов. Кроме этого, он может быть перегружен, чтобы принимать любое количество дополнительных аргументов, включая ноль.Вот пример синтаксиса:
Применение:
Во всей стандартной библиотеке C ++ объекты функций всегда копируются. Поэтому ваши собственные функциональные объекты должны быть дешевыми для копирования. Если функциональному объекту абсолютно необходимо использовать данные, которые дорого копировать, лучше хранить эти данные в другом месте и обращаться к ним с помощью функционального объекта.
Операторы сравнения
Операторы сравнения двоичного инфикса должны, согласно практическим правилам, быть реализованы как функции, не являющиеся членами 1 . Одинарный префикс отрицания
!
должно (согласно тем же правилам) быть реализовано как функция-член. (но обычно не стоит перегружать его.)Алгоритмы стандартной библиотеки (например
std::sort()
) и типы (напримерstd::map
) всегда ожидаютoperator<
присутствия. Однако пользователи вашего типа будут ожидать, что будут присутствовать и все остальные операторы , поэтому, если вы определитеoperator<
, обязательно следуйте третьему фундаментальному правилу перегрузки операторов, а также определите все другие логические операторы сравнения. Канонический способ их реализации заключается в следующем:Здесь важно отметить, что только два из этих операторов на самом деле что-то делают, остальные просто передают свои аргументы любому из этих двух, чтобы выполнить реальную работу.
Синтаксис для перегрузки оставшихся двоичных логических операторов (
||
,&&
) соответствует правилам операторов сравнения. Тем не менее, очень маловероятно, что вы найдете разумный вариант использования этих 2 .1 Как и все эмпирические правила, иногда могут быть и причины нарушать это правило. Если это так, не забывайте, что левый операнд бинарных операторов сравнения, который будет для функций-членов , тоже
*this
должен бытьconst
. Таким образом, оператор сравнения, реализованный как функция-член, должен иметь эту сигнатуру:(Обратите внимание
const
на в конце.)2 Следует отметить, что во встроенной версии
||
и&&
используется ярлык семантики. В то время как пользовательские (потому что они являются синтаксическим сахаром для вызовов методов) не используют сокращенную семантику. Пользователь будет ожидать, что эти операторы будут иметь семантику ярлыков, и их код может зависеть от этого, поэтому настоятельно рекомендуется НИКОГДА не определять их.Арифметические Операторы
Унарные арифметические операторы
Унарные операторы инкремента и декремента бывают как префиксные, так и постфиксные. Чтобы отличить одно от другого, варианты postfix принимают дополнительный фиктивный аргумент int. Если вы перегружаете инкремент или декремент, убедитесь, что вы всегда используете как префиксную, так и постфиксную версии. Вот каноническая реализация инкремента, декремент следует тем же правилам:
Обратите внимание, что постфиксный вариант реализован в терминах префикса. Также обратите внимание, что postfix делает дополнительную копию. 2
Перегрузка унарного минуса и плюса не очень распространена и, вероятно, лучше избегать. При необходимости они, вероятно, должны быть перегружены как функции-члены.
2 Также обратите внимание, что вариант с постфиксом выполняет больше работы и поэтому менее эффективен в использовании, чем вариант с префиксом. Это хорошая причина, как правило, предпочитать увеличение префикса над увеличением постфикса. Хотя компиляторы обычно могут оптимизировать дополнительную работу приращения постфикса для встроенных типов, они могут быть не в состоянии сделать то же самое для пользовательских типов (которые могут выглядеть невинно, как итератор списка). Как только вы привыкли делать
i++
, становится очень трудно помнить, чтобы делать++i
вместо этого, когдаi
он не имеет встроенного типа (плюс вам придется менять код при смене типа), так что лучше всегда иметь привычку используя приставку префикса, если постфикс не требуется явно.Бинарные арифметические операторы
Для бинарных арифметических операторов не забывайте соблюдать перегрузку третьего оператора основного правила: если вы предоставляете
+
, также предоставляете+=
, если вы предоставляете-
, не опускайте-=
и т. Д. Говорят, что Эндрю Кениг был первым, кто заметил, что составное присваивание операторы могут быть использованы в качестве базы для своих несоставных аналогов. То есть оператор+
реализован в терминах+=
,-
реализован в терминах-=
и т. Д.Согласно нашим практическим правилам,
+
и его компаньоны должны быть нечленами, а их составные аналоги присваивания (+=
и т. Д.), Изменяя свой левый аргумент, должны быть членами. Вот примерный код для+=
и+
; другие двоичные арифметические операторы должны быть реализованы таким же образом:operator+=
возвращает свой результат по ссылке, аoperator+
возвращает копию своего результата. Конечно, возврат ссылки обычно более эффективен, чем возврат копии, но в случаеoperator+
с копированием нет никакого способа. Когда вы пишетеa + b
, вы ожидаете, что результатом будет новое значение, поэтомуoperator+
должно возвращать новое значение. 3 Также обратите внимание, чтоoperator+
левый операнд принимает копию, а не константную ссылку. Причина этого та же, что и дляoperator=
принятия аргумента за копию.Операторы битовых манипуляций
~
&
|
^
<<
>>
должны быть реализованы так же, как арифметические операторы. Однако (за исключением перегрузки<<
и>>
вывода и ввода) существует очень мало разумных вариантов их использования.3 Опять же, урок, который следует извлечь из этого, заключается в том
a += b
, что в целом он более эффективен, чемa + b
и должен быть предпочтительным, если это возможно.Подписка на массив
Оператор индекса массива - это двоичный оператор, который должен быть реализован как член класса. Он используется для контейнероподобных типов, которые позволяют доступ к их элементам данных по ключу. Каноническая форма предоставления этого такова:
Если вы не хотите, чтобы пользователи вашего класса могли изменять элементы данных, возвращаемые
operator[]
(в этом случае вы можете опустить неконстантный вариант), вы всегда должны указывать оба варианта оператора.Если известно, что value_type ссылается на встроенный тип, константный вариант оператора должен лучше возвращать копию вместо константной ссылки:
Операторы для Pointer-подобных типов
Для определения ваших собственных итераторов или умных указателей вы должны перегрузить оператор разыменования унарного префикса
*
и оператор доступа к двоичному инфиксному указателю->
:Обратите внимание, что они также почти всегда нуждаются как в const, так и в неконстантной версии. Для
->
оператора ifvalue_type
имеетclass
(илиstruct
илиunion
) тип, другойoperator->()
вызывается рекурсивно, пока не будетoperator->()
возвращено значение не-классового типа.Унарный адрес оператора никогда не должен быть перегружен.
Для
operator->*()
просмотра этого вопроса . Он редко используется и, следовательно, редко перегружен. На самом деле, даже итераторы не перегружают его.Перейти к операторам преобразования
источник
operator->()
на самом деле очень странно. Не требуется возвращать avalue_type*
- фактически, он может возвращать другой тип класса, при условии, что этот тип класса имеетoperator->()
, который затем будет вызван. Этот рекурсивный вызовoperator->()
s продолжается до тех пор, пока неvalue_type*
произойдет возвращаемый тип. Безумие! :)*
с точки зрения,*=
но это было бы неудобно, потому что одной из первых операций*=
было бы создать новый объект, результат вычислений. Затем, после цикла for-ijk, мы поменяли этот временный объект на*this
. то есть. 1. Копия, 2. Оператор *, 3. ПоменятьсяT* const
возвратconst T&
разыменования при разыменовании, а это не так. Или, другими словами: указатель const не подразумевает указателя const. Фактически, это не тривиально, чтобы подражатьT const *
- что является причиной всегоconst_iterator
материала в стандартной библиотеке. Вывод: подпись должна бытьreference_type operator*() const; pointer_type operator->() const
L <= R
также можно выразить как!(R < L)
вместо!(L > R)
. Можно сохранить дополнительный слой встраивания в трудно оптимизируемых выражениях (а также в Boost.Operators).Три основных правила перегрузки операторов в C ++
Когда дело доходит до перегрузки операторов в C ++, следует соблюдать три основных правила . Как и во всех таких правилах, действительно есть исключения. Иногда люди отклонялись от них, и в результате получился неплохой код, но таких положительных отклонений мало, и они далеко друг от друга. По крайней мере, 99 из 100 таких отклонений, которые я видел, были неоправданными. Тем не менее, это может быть и 999 из 1000. Так что вам лучше придерживаться следующих правил.
Всякий раз, когда значение оператора не является явно ясным и бесспорным, его не следует перегружать. Вместо этого предоставьте функцию с правильно выбранным именем.
По сути, первое и главное правило перегрузки операторов в самом его сердце гласит: не делайте этого . Это может показаться странным, потому что о перегрузке операторов известно очень много, поэтому многие статьи, главы книг и другие тексты имеют дело со всем этим. Но, несмотря на это, казалось бы, очевидное свидетельство, лишь на удивление мало случаев, когда уместна перегрузка оператора., Причина в том, что на самом деле трудно понять семантику, лежащую в основе применения оператора, если использование оператора в области приложения хорошо известно и не оспаривается. Вопреки распространенному мнению, это вряд ли когда-либо.
Всегда придерживайтесь известной семантики оператора.
C ++ не накладывает ограничений на семантику перегруженных операторов. Ваш компилятор с радостью примет код, который реализует двоичный
+
оператор для вычитания из его правого операнда. Однако пользователи такого оператора никогда не заподозрят, что выражениеa + b
вычитаетсяa
изb
. Конечно, это предполагает, что семантика оператора в области приложения неоспорима.Всегда предоставляйте все из набора связанных операций.
Операторы связаны друг с другом и с другими операциями. Если ваш тип поддерживает
a + b
, пользователи также могут ожидать звонкаa += b
. Если он поддерживает приращение префикса++a
, они также будутa++
работать. Если они могут проверить, могут лиa < b
они также наверняка быть в состоянии проверить, есть лиa > b
. Если они могут копировать-конструировать ваш тип, они ожидают, что назначение также будет работать.Перейти к решению между членом и не членом .
источник
boost::spirit
LOL.+
для конкатенации строк является нарушением, но к настоящему моменту оно стало общепринятой практикой, так что это кажется естественным. Хотя я помню класс строк домашнего варки, который я видел в 90-х годах, который использовал двоичный файл&
для этой цели (см. BASIC для установленной практики). Но, да, включение этого в стандартную библиотеку в основном делает это в камне. То же самое касается злоупотребления<<
и>>
для IO, кстати. Почему сдвиг влево будет очевидной операцией вывода? Потому что мы все узнали об этом, когда увидели наше первое «Привет, мир!» применение. И ни по какой другой причине.operator==
том, что это должно быть отношение эквивалентности (IOW, вы не должны использовать не сигнальный NaN). Есть много полезных отношений эквивалентности на контейнерах. Что означает равенство? «a
равноb
» означает, чтоa
иb
имеют одинаковое математическое значение. Понятие математического значения (не-NaN)float
понятно, но математическое значение контейнера может иметь много различных (рекурсивных) полезных определений. Самое сильное определение равенства - «это одни и те же объекты», и оно бесполезно.Общий синтаксис перегрузки операторов в C ++
Вы не можете изменить значение операторов для встроенных типов в C ++, операторы могут быть перегружены только для пользовательских типов 1 . То есть, по крайней мере, один из операндов должен быть пользовательского типа. Как и в случае с другими перегруженными функциями, операторы могут быть перегружены для определенного набора параметров только один раз.
Не все операторы могут быть перегружены в C ++. Среди операторов, которые не могут быть перегружены:
.
::
sizeof
typeid
.*
и единственный троичный оператор в C ++,?:
Среди операторов, которые могут быть перегружены в C ++, это:
+
-
*
/
%
и+=
-=
*=
/=
%=
(все двоичные инфиксы);+
-
(одинарный префикс);++
--
(одинарный префикс и постфикс)&
|
^
<<
>>
и&=
|=
^=
<<=
>>=
(все двоичные инфиксы);~
(одинарный префикс)==
!=
<
>
<=
>=
||
&&
(все двоичные инфиксы);!
(одинарный префикс)new
new[]
delete
delete[]
=
[]
->
->*
,
(все двоичные инфиксы);*
&
(все унарные префиксы)()
(вызов функции, n-арный инфикс)Однако тот факт, что вы можете перегружать все это, не означает, что вы должны это делать. Смотрите основные правила перегрузки операторов.
В C ++ операторы перегружены в виде функций со специальными именами . Как и в случае с другими функциями, перегруженные операторы обычно могут быть реализованы либо как функции-члены типа их левого операнда, либо как функции, не являющиеся членами . Вольны ли вы выбирать или обязаны использовать один из них, зависит от нескольких критериев. 2 Унарный оператор
@
3 , примененный к объекту x, вызывается либо как,operator@(x)
либо какx.operator@()
. Двоичный инфиксный оператор@
, применяемый к объектамx
иy
, вызывается какoperator@(x,y)
или какx.operator@(y)
. 4Операторы, которые реализованы как функции, не являющиеся членами, иногда являются друзьями типа своего операнда.
1 Термин «пользовательский» может вводить в заблуждение. C ++ делает различие между встроенными типами и пользовательскими типами. К первым относятся, например, int, char и double; к последним относятся все типы struct, class, union и enum, в том числе из стандартной библиотеки, даже если они как таковые не определены пользователями.
2 Это рассматривается в более поздней части этого FAQ.
3 В
@
C ++ оператор недопустим, поэтому я использую его в качестве заполнителя.4 Единственный троичный оператор в C ++ не может быть перегружен, и единственный n-арный оператор всегда должен быть реализован как функция-член.
Перейдите к трем основным правилам перегрузки операторов в C ++ .
источник
~
это унарный префикс, а не двоичный инфикс..*
отсутствует в списке не перегружаемых операторов.:)
operator+()
функцию-член, но даете ей подпись свободной функции. Смотрите здесь .Решение между членом и не членом
Бинарные операторы
=
(присваивание),[]
(подписка на массив),->
(доступ к элементу), а также()
оператор n-ary (вызов функции) всегда должны быть реализованы как функции-члены , поскольку синтаксис языка требует их.Другие операторы могут быть реализованы как члены или не члены. Некоторые из них, однако, обычно должны быть реализованы как функции, не являющиеся членами, потому что вы не можете изменить их левый операнд. Наиболее выдающимися из них являются операторы ввода и вывода,
<<
а>>
левые операнды - это потоковые классы из стандартной библиотеки, которые нельзя изменить.Для всех операторов, для которых вам необходимо выбрать их реализацию в качестве функции-члена или функции, не являющейся членом, используйте следующие практические правила, чтобы принять решение:
Конечно, как со всеми эмпирическими правилами, есть исключения. Если у вас есть тип
и вы хотите перегрузить операторы инкремента и декремента для него, вы не можете сделать это как функции-члены, так как в C ++ типы enum не могут иметь функции-члены. Таким образом, вы должны перегрузить его как бесплатную функцию. А также
operator<()
для шаблона класса, вложенного в шаблон класса, гораздо проще писать и читать, когда он выполняется как функция-член, встроенная в определение класса. Но это действительно редкие исключения.(Однако, если вы делаете исключение, не забывайте проблему
const
-ness для операнда, который для функций-членов становится неявнымthis
аргументом. Если оператор в качестве функции, не являющейся членом, будет принимать крайний левый аргумент в качествеconst
ссылки , тот же оператор , как функции члена должен иметьconst
на конце , чтобы сделать*this
вconst
ссылку.)Перейдите к общим операторам для перегрузки .
источник
operator+=()
не быть членом. Он должен изменить свой левый операнд, поэтому по определению он должен копаться вглубь. Что бы вы получили, не сделав его членом?operator +=
и вappend
методах. Этотappend
метод является более полным, потому что вы можете добавить подстроку параметра из индекса i в индекс n -1:append(string, start, end)
кажется логичным, чтобы+=
вызов добавлялся с помощьюstart = 0
иend = string.size
. В этот момент append может быть методом-участником, ноoperator +=
не обязательно должен быть участником, а если он не будет членом, это уменьшит количество кода, играющего с внутренними типами String, так что это хорошая вещь ... ^ _ ^ ...Операторы преобразования (также известные как пользовательские преобразования)
В C ++ вы можете создавать операторы преобразования, которые позволяют компилятору преобразовывать между вашими типами и другими определенными типами. Существует два типа операторов преобразования: неявные и явные.
Операторы неявного преобразования (C ++ 98 / C ++ 03 и C ++ 11)
Оператор неявного преобразования позволяет компилятору неявно преобразовывать (например, преобразование между
int
иlong
) значение пользовательского типа в некоторый другой тип.Ниже приведен простой класс с оператором неявного преобразования:
Операторы неявного преобразования, такие как конструкторы с одним аргументом, являются пользовательскими преобразованиями. Компиляторы будут предоставлять одно пользовательское преобразование при попытке сопоставить вызов перегруженной функции.
Поначалу это кажется очень полезным, но проблема в том, что неявное преобразование включается даже тогда, когда оно не ожидается. В следующем коде
void f(const char*)
будет вызван, потому чтоmy_string()
не является lvalue , поэтому первый не совпадает:Начинающие легко ошибаются, и даже опытные программисты на C ++ иногда удивляются, потому что компилятор выбирает перегрузку, которую они не подозревали. Эти проблемы могут быть смягчены явными операторами преобразования.
Операторы явного преобразования (C ++ 11)
В отличие от операторов неявного преобразования, операторы явного преобразования никогда не сработают, если вы этого не ожидаете. Ниже приведен простой класс с явным оператором преобразования:
Обратите внимание на
explicit
. Теперь, когда вы пытаетесь выполнить неожиданный код из операторов неявного преобразования, вы получаете ошибку компилятора:Чтобы вызвать явный оператор приведения, вы должны использовать приведение
static_cast
в стиле C или приведение в стиле конструктора (т.е.T(value)
).Однако есть одно исключение: компилятору разрешено неявное преобразование в
bool
. Кроме того, компилятору не разрешается делать другое неявное преобразование после его преобразования вbool
(компилятору разрешено делать 2 неявных преобразования за раз, но только 1 пользовательское преобразование при максимуме).Поскольку компилятор не будет приводить «прошлое»
bool
, явные операторы преобразования теперь устраняют необходимость использования идиомы Safe Bool . Например, умные указатели до C ++ 11 использовали идиому Safe Bool для предотвращения преобразований в целочисленные типы. В C ++ 11 интеллектуальные указатели вместо этого используют явный оператор, потому что компилятору не разрешается неявно преобразовывать в целочисленный тип после того, как он явно преобразовал тип в bool.Продолжайте перегрузку
new
иdelete
.источник
Перегрузка
new
иdelete
Примечание. Это касается только синтаксиса перегрузки,
new
аdelete
не реализации таких перегруженных операторов. Я думаю, что семантика перегрузкиnew
иdelete
заслуживает своего собственного FAQ , в рамках темы перегрузки операторов я никогда не смогу отдать должное.основы
В C ++, когда вы пишете новое выражение , как
new T(arg)
две вещи происходят , когда это выражение вычисляется: Сначалаoperator new
вызывается для получения сырой памяти, а затем соответствующий конструкторT
вызывается , чтобы превратить эту сырую память в действительный объект. Аналогично, когда вы удаляете объект, сначала вызывается его деструктор, а затем возвращается памятьoperator delete
.C ++ позволяет настраивать обе эти операции: управление памятью и построение / уничтожение объекта в выделенной памяти. Последнее делается путем написания конструкторов и деструкторов для класса. Точная настройка управления памятью осуществляется путем написания собственных
operator new
иoperator delete
.Первое из основных правил перегрузки операторов - не делайте этого - особенно относится к перегрузкам
new
иdelete
. Почти единственными причинами перегрузки этих операторов являются проблемы с производительностью и нехватка памяти , и во многих случаях другие действия, такие как изменение используемых алгоритмов , обеспечат гораздо более высокое соотношение цена / выигрыш, чем попытка настроить управление памятью.Стандартная библиотека C ++ поставляется с набором предопределенных
new
иdelete
операторов. Самые важные из них:Первые два выделяют / освобождают память для объекта, последние два - для массива объектов. Если вы предоставите свои собственные версии, они не будут перегружены, а заменят версии из стандартной библиотеки.
Если вы перегружаете
operator new
, вы также должны всегда перегружать сопоставлениеoperator delete
, даже если вы никогда не намереваетесь его вызывать. Причина в том, что, если конструктор выдает во время вычисления нового выражения, система времени выполнения вернет память вoperator delete
соответствие с тем,operator new
который был вызван, чтобы выделить память для создания объекта. Если вы не предоставите соответствующийoperator delete
по умолчанию вызывается, что почти всегда неверно.Если вы перегружаете
new
иdelete
, вы должны также рассмотреть возможность перегрузки вариантов массива.размещение
new
C ++ позволяет новым и удаляемым операторам принимать дополнительные аргументы.
Так называемое размещение новых позволяет вам создать объект по определенному адресу, который передается:
Стандартная библиотека поставляется с соответствующими перегрузками операторов new и delete для этого:
Обратите внимание, что в приведенном выше примере кода для размещения нового
operator delete
никогда не вызывается, если только конструктор X не выдает исключение.Вы также можете перегружать
new
иdelete
другими аргументами. Как и в случае с дополнительным аргументом для размещения new, эти аргументы также перечислены в скобках после ключевого словаnew
. По историческим причинам такие варианты часто также называют размещением новых, даже если их аргументы не предназначены для размещения объекта по определенному адресу.Новый класс и удалить новый
Чаще всего вам потребуется более тонкая настройка управления памятью, поскольку измерения показали, что экземпляры определенного класса или группы связанных классов часто создаются и уничтожаются, а управление памятью по умолчанию системы времени выполнения настроено для общая производительность, неэффективно в этом конкретном случае. Чтобы улучшить это, вы можете перегрузить new и удалить для определенного класса:
Перегруженные таким образом, new и delete ведут себя как статические функции-члены. Для объектов
my_class
, тоstd::size_t
аргумент будет всегдаsizeof(my_class)
. Однако эти операторы также вызываются для динамически размещаемых объектов производных классов , и в этом случае они могут быть больше, чем это.Глобальный новый и удалить
Чтобы перегрузить глобальное новое и удалить, просто замените предопределенные операторы стандартной библиотеки нашими собственными. Однако это редко когда-либо нужно делать.
источник
nothrow
новое.Почему
operator<<
функция потоковой передачи объектовstd::cout
в файл или в файл не может быть функцией-членом?Допустим, у вас есть:
Учитывая это, вы не можете использовать:
Поскольку
operator<<
перегружен как функция-членFoo
, LHS оператора должен бытьFoo
объектом. Это означает, что вы должны будете использовать:что очень не интуитивно понятно.
Если вы определите его как функцию, не являющуюся членом,
Вы сможете использовать:
что очень интуитивно понятно
источник