Плохо ли использовать компилятор C ++ только для перегрузки функций?
ИМХО точка зрения, да, и мне нужно стать шизофреником, чтобы ответить на этот вопрос, поскольку я люблю оба языка, но это не имеет ничего общего с эффективностью, а скорее с безопасностью и идиоматическим использованием языков.
C сторона
С точки зрения C, я считаю настолько расточительным, чтобы ваш код требовал C ++ просто для использования перегрузки функций. Если вы не используете его для статического полиморфизма с шаблонами C ++, это такой тривиальный синтаксический сахар, полученный в обмен на переключение на совершенно другой язык. Кроме того, если вы когда-нибудь захотите экспортировать свои функции в dylib (может быть, а может и не иметь практического значения), вы больше не сможете делать это практически для широкого использования со всеми именованными символами.
Сторона C ++
С точки зрения C ++, вы не должны использовать C ++ как C с перегрузкой функций. Это не стилистический догматизм, а связанный с практическим использованием повседневного C ++.
Ваш обычный вид кода на C достаточно разумно и «безопасно» писать, если вы работаете с системой типов C, которая запрещает такие вещи, как копирование ctors в structs
. Как только вы работаете в гораздо более богатой системе типов C ++, ежедневные функции, которые имеют огромное значение, например, memset
и memcpy
не становятся функциями, вам следует постоянно полагаться. Вместо этого это функции, которых вы обычно хотите избежать, как чумы, поскольку с типами C ++ вам не следует рассматривать их как необработанные биты и байты, которые нужно копировать, перетасовывать и освобождать. Даже если в данный момент ваш код использует только такие вещи, как memset
примитивы и UDT POD, в тот момент, когда кто-либо добавляет ctor к любому используемому вами UDT (включая просто добавление члена, для которого он нужен, например,std::unique_ptr
член) против таких функций или виртуальной функции или чего-либо в этом роде, она делает все ваше обычное кодирование в стиле C восприимчивым к неопределенному поведению. Возьми это у самого Херба Саттера:
memcpy
и memcmp
нарушать систему типов. Использование memcpy
для копирования объектов похоже на зарабатывание денег с помощью ксерокса. Использование memcmp
для сравнения объектов похоже на сравнение леопардов путем подсчета их пятен. Инструменты и методы могут показаться выполненными, но они слишком грубые, чтобы делать это приемлемо. Все объекты C ++ предназначены для сокрытия информации (возможно, самый прибыльный принцип в разработке программного обеспечения; см. Пункт 11): объекты скрывают данные (см. Пункт 41) и разрабатывают точные абстракции для копирования этих данных с помощью конструкторов и операторов присваивания (см. Пункты с 52 по 55) , Преодоление всего этого memcpy
является серьезным нарушением сокрытия информации и часто приводит к утечкам памяти и ресурсов (в лучшем случае), сбоям (хуже) или неопределенному поведению (хуже всего) - стандартам кодирования C ++.
Многие разработчики C не согласны с этим, и это справедливо, поскольку философия применима только в том случае, если вы пишете код на C ++. Вы , скорее всего , будете писать очень проблемный код , если вы используете такие функции , как memcpy
все время в коде , который строит , как C ++ , но это совершенно нормально , если вы делаете это в C . Эти два языка сильно отличаются в этом отношении из-за различий в системе типов. Очень заманчиво взглянуть на подмножество функций, общих для этих двух, и поверить, что одну можно использовать как другую, особенно на стороне C ++, но код C + (или код C--), как правило, гораздо более проблематичен, чем и C, и C ++ код.
Аналогично, вы не должны использовать, скажем, malloc
в контексте стиля C (что подразумевает отсутствие EH), если он может напрямую вызывать любые функции C ++, которые могут генерировать, поскольку в результате вы получаете неявную точку выхода в своей функции в результате исключение, которое вы не можете эффективно отловить при написании кода в стиле C, до того, как сможете использовать free
эту память. Таким образом , всякий раз , когда у вас есть файл , который строит в C ++ с .cpp
расширением или что - то , и он делает все эти типы вещей , как malloc
, memcpy
, memset
,qsort
и т. д., тогда он задает проблемы в дальнейшем, если это не так, если только это не деталь реализации класса, который работает только с примитивными типами, и в этот момент ему по-прежнему необходимо выполнять обработку исключений, чтобы быть безопасным для исключений. Если вы пишете код на Си ++ вместо этого вы хотите , чтобы в основном полагаться на RAII и использовать такие вещи , как vector
, unique_ptr
, shared_ptr
и т.д., и избежать все нормальное кодирования C-стиля , когда это возможно.
Причина, по которой вы можете играть с бритвенными лезвиями в C и рентгеновских типах данных и играть с их битами и байтами, не будучи склонной к побочному ущербу в команде (хотя вы все равно можете по-настоящему навредить себе в любом случае), не в том, что C типы могут делать, но из-за того, что они никогда не смогут сделать, В тот момент, когда вы расширите систему типов C, включив в нее такие функции C ++, как ctors, dtors и vtables, а также обработку исключений, весь идиоматический код C будет представлен намного, гораздо более опасным, чем в настоящее время, и вы увидите новый вид развитие философии и менталитета, которые будут поощрять совершенно другой стиль кодирования, как вы видите в C ++, который теперь рассматривает даже использование грубых указателей на ошибки для класса, который управляет памятью, в отличие от, скажем, ресурса, соответствующего RAII unique_ptr
. Это мышление не развивалось из абсолютного чувства безопасности. Он развился из того, что C ++ определенно должен быть защищен от таких функций, как обработка исключений, учитывая то, что он просто позволяет использовать в своей системе типов.
Исключение-безопасность
Опять же, в тот момент, когда вы находитесь на земле C ++, люди будут ожидать, что ваш код будет безопасным для исключений. Люди могут поддерживать ваш код в будущем, учитывая, что он уже написан и компилируется в C ++, и просто используется std::vector, dynamic_cast, unique_ptr, shared_ptr
и т. Д. В коде, который прямо или косвенно вызывается вашим кодом, полагая, что он безвреден, поскольку ваш код уже «предположительно» C ++. код. В этот момент мы сталкиваемся с вероятностью того, что что-то произойдет, и тогда, когда вы возьмете совершенно прекрасный и красивый C-код, подобный этому:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
... сейчас сломано. Это должно быть переписано, чтобы быть безопасным от исключений:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
Валовой! Вот почему большинство разработчиков C ++ требовали бы этого:
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
Выше приведен RAII-совместимый безопасный код исключения, который обычно одобряют разработчики C ++, так как функция не пропустит, какая строка кода вызывает неявный выход в результате a throw
.
Выбрать язык
Вы должны либо принять систему типов и философию C ++ с RAII, безопасностью исключений, шаблонами, ООП и т. Д., Либо использовать C, который в основном вращается вокруг необработанных битов и байтов. Вы не должны создавать нечестивый брак между этими двумя языками, а вместо этого разделять их на разные языки, чтобы относиться к ним по-разному, а не стирать их вместе.
Эти языки хотят жениться на тебе. Вы обычно должны выбрать один вместо того, чтобы встречаться и дурачиться с обоими. Или вы можете быть многоженцем, как я, и жениться на обоих, но вы должны полностью поменять свое мышление, проводя время друг с другом, и держать их хорошо отделенными друг от друга, чтобы они не сражались друг с другом.
Бинарный размер
Просто из любопытства я попытался взять мою реализацию и тесты для бесплатного списка прямо сейчас и перенести ее на C ++, так как мне стало действительно интересно:
[...] не знаю, как это будет выглядеть для C, потому что я не использовал компилятор C.
... и хотел знать, будет ли размер бинарного файла увеличиваться при создании C ++. Мне потребовалось разбросать явное приведение повсюду, что было бесполезно (одна из причин, по которой мне больше нравится писать вещи низкого уровня, такие как распределители и структуры данных в C), но заняло всего минуту.
Это было просто сравнение 64-разрядной сборки MSVC для простого консольного приложения и с кодом, который не использовал никаких функций C ++, даже перегрузку операторов - только разница между созданием его с C и использованием, скажем, <cstdlib>
вместо <stdlib.h>
и такие вещи, но я был удивлен, обнаружив, что это не имеет никакого значения к размеру двоичного файла!
Двоичный файл был 9,728
байтовым, когда он был встроен в C, а также 9,278
байтами, когда он был скомпилирован как код C ++. Я на самом деле не ожидал этого. Я думал, что такие вещи, как EH, по крайней мере, добавят туда немного (думал, что это будет, по крайней мере, как сто байт по-другому), хотя, вероятно, он смог понять, что нет необходимости добавлять инструкции, связанные с EH, так как я просто используя стандартную библиотеку C и ничего не выбрасывает. Я думал что-тодобавил бы немного к двоичному размеру в любом случае, как RTTI. Во всяком случае, это было круто видеть это. Конечно, я не думаю, что вы должны обобщать этот один результат, но это, по крайней мере, меня немного впечатлило. Это также не оказало никакого влияния на тесты, и, естественно, так, поскольку я представляю, что идентичный результирующий размер двоичного файла также означал идентичные результирующие машинные инструкции.
Тем не менее, кому небезразличен размер двоичного файла в связи с вопросами безопасности и разработки, упомянутыми выше? Итак, еще раз, выберите язык и примите его философию вместо того, чтобы пытаться убить его; это то, что я рекомендую.
//
комментировать. Если это работает, почему бы и нет?