Чи погана практика використовувати компілятор C ++ лише для перевантаження функцій?


60

Тому я працюю над розробкою програмного забезпечення, використовуючи C для певного процесора. Набір інструментів включає можливість компілювати як C, так і C ++. Для того, що я роблю, у цьому середовищі немає динамічного розподілу пам’яті, а програма загалом досить проста. Не кажучи вже про те, що у пристрою майже немає потужності або ресурсів процесора. Насправді немає жодної необхідності використовувати будь-який C ++.

Попри це, є кілька місць, де я виконую функцію перевантаження (особливість C ++). Мені потрібно надіслати кілька різних типів даних, і мені не хочеться використовувати printfформат стилю за допомогою якогось %s(або будь-якого іншого) аргументу. Я бачив деяких людей, які не мали доступу до компілятора C ++, який робив printfце, але в моєму випадку доступна підтримка C ++.

Тепер я впевнений, що у мене може виникнути питання, чому для початку мені потрібно перевантажувати функцію. Тому я спробую зараз відповісти на це. Мені потрібно передавати різні типи даних із послідовного порту, тому у мене є кілька перевантажень, які передають такі типи даних:

unsigned char*
const char*
unsigned char
const char

Я просто вважаю за краще не мати одного методу, який би обробляв усі ці речі. Коли я запускаю функцію, я просто хочу, щоб вона передала послідовний порт, у мене не так багато ресурсів, тому я не хочу робити що- небудь інше, але мою передачу.

Хтось ще побачив мою програму і запитав мене: "для чого ти використовуєш файли CPP?" Отже, це моя єдина причина. Це погана практика?

Оновлення

Я хотів би вирішити декілька заданих питань:

Об'єктивна відповідь на вашу дилему буде залежати від:

  1. Чи значно зростає розмір виконуваного файлу, якщо ви використовуєте C ++.

На сьогодні розмір виконуваного файлу споживає 4,0% програмної пам'яті (із 5248 байт) та 8,3% пам'яті даних (із 342 байтів). Тобто компіляція для C ++ ... Я не знаю, як це виглядатиме для C, оскільки я не використовував компілятор C. Я знаю, що ця програма більше не зростатиме, тому, наскільки обмежені ресурси, я б сказав, що я добре там ...

  1. Чи є помітний негативний вплив на продуктивність, якщо ви використовуєте C ++.

Ну якщо є, я нічого не помічав ... але знову ж таки, тому я можу задати це питання, оскільки я не повністю розумію.

  1. Чи може код використовувати повторно на іншій платформі, де доступний лише компілятор C.

Я знаю, що відповіді на це, безумовно, немає . Ми насправді розглядаємо можливість переходу на інший процесор, але лише більш потужні процесори на базі ARM (усі, про які я знаю, фактично мають ланцюги інструментів компілятора C ++).


59
Мені відомо, що використовую C ++ для проекту лише з використанням функцій C, щоб я мав //коментарі. Якщо це працює, чому б і ні?
Жуль

76
Поганою практикою було б обмеження себе на C, коли ви добре використовуєте функції, які він не надає.
Джеррі Труну

35
Коли ви говорите "використовувати компілятор C ++", ви маєте на увазі "використовувати C ++". Просто скажи. Ви не можете компілювати C за допомогою компілятора C ++, але ви можете легко перейти з C на C ++, що саме ви насправді робите.
користувач253751

4
"Для того, що я роблю, немає динамічного розподілу пам'яті на процесор, а програма в цілому досить проста. Не кажучи вже про те, що у пристрою майже немає потужності або ресурсів процесора. Дійсно немає необхідності використовувати будь-який C ++." Я сподіваюсь, що перше з цих двох речень, мабуть, стане причиною не використовувати C ++, оскільки вони досить погані, якщо вони є. C ++ ідеально підходить для вбудованих систем.
Фарап

21
@Jules Я впевнений, що ти це знаєш і певний час думав, але, якщо хтось це читає, це не так: //коментарі були в стандарті C з C99.
Девіслор

Відповіді:


77

Я б не став заходити так далеко, щоб назвати це «погана практика» сама по собі , але ні я переконаний , що це дійсно правильне рішення вашої проблеми. Якщо все, що вам потрібно, це чотири окремі функції для виконання чотирьох типів даних, чому б не зробити те, що програмісти C робили з незапам’ятних часів:

void transmit_uchar_buffer(unsigned char *buffer);
void transmit_char_buffer(char *buffer);
void transmit_uchar(unsigned char c);
void transmit_char(char c);

Це ефективно те, що компілятор C ++ так чи інакше робить поза кадром, і це не так вже й великі накладні витрати для програміста. Уникає всіх проблем "чому ти пишеш не зовсім-С із компілятором C ++", і означає, що ніхто більше у вашому проекті не буде плутати, які біти C ++ "дозволені", а які біти - ні.


8
Я б сказав, чому це не так, тому що переданий тип є (ймовірно) деталлю реалізації, тому його приховування та надання можливості компілятору займатися вибором реалізації може призвести до більш читабельного коду. І якщо використання функції C ++ покращує читабельність, то чому б цього не зробити?
Жуль

29
Можна навіть #defineпередавати () за допомогою C11_Generic
Deduplicator

16
@Jules Тому що це дуже заплутано щодо того, які функції C ++ дозволяється використовувати в проекті. Чи буде відхилена потенційна зміна, що містить об’єкти? Що з шаблонами? Коментарі у стилі C ++? Звичайно, ви можете обійти це документом у стилі кодування, але якщо все, що ви робите, це простий випадок перевантаження функцій, тоді просто напишіть C.
Філіп Кендалл

25
@Phillip "коментарі у стилі C ++" є дійсними C протягом більше десяти років.
Девід Конрад

12
@Jules: хоча переданий тип, ймовірно, є детальною інформацією про реалізацію програмного забезпечення, яке робить котирування страхування, програма OPs звучить як вбудована система, яка здійснює послідовне спілкування, де тип і розмір даних мають важливе значення.
whatsisname

55

Використання лише деяких функцій C ++, інакше трактувати його як C не є звичайним явищем, але також не є абсолютно нечуваним жодним із них. Насправді, деякі люди навіть не використовують жодних функцій C ++, за винятком суворої та потужнішої перевірки типу. Вони просто пишуть C (дбаючи про те, щоб писати лише у загальному перетині C ++ і C), потім компілюють компілятор C ++ для перевірки типів і компілятор C для генерації коду (або просто дотримуються компілятор C ++ до кінця).

Linux - це приклад бази даних коду, де люди регулярно просять, щоб такі ідентифікатори, як classперейменовані на, klassабо kclass, щоб вони могли компілювати Linux з компілятором C ++. Очевидно, враховуючи думку Лінуса щодо C ++ , вони завжди збиваються :-D GCC - це приклад кодової бази, яку спочатку перетворили на "C ++ - чистий C", а потім поступово відновили для використання більше функцій C ++.

Немає нічого поганого в тому, що ти робиш. Якщо ви справді параноїчні щодо якості генерації коду свого компілятора C ++, ви можете використовувати компілятор типу Comeau C ++, який компілюється в C як свою цільову мову, а потім використовувати компілятор C. Таким чином, ви також можете здійснити точкові перевірки коду, щоб побачити, чи використовуючи C ++, вводить будь-який непередбачуваний код, залежний від продуктивності. Однак це не повинно бути просто перевантаженням, яке буквально автоматично автоматично генерує різні функції, які називаються - IOW, саме те, що ви робите в C у будь-якому випадку.


15

Об'єктивна відповідь на вашу дилему буде залежати від:

  1. Чи значно зростає розмір виконуваного файлу, якщо ви використовуєте C ++.
  2. Чи є помітний негативний вплив на продуктивність, якщо ви використовуєте C ++.
  3. Чи може код використовувати повторно на іншій платформі, де доступний лише компілятор C.

Якщо відповіді на будь-яке з питань "так", вам може бути краще створити різні імена функцій для різних типів даних та дотримуватися C.

Якщо відповіді на всі запитання "ні", я не бачу причини, чому не слід використовувати C ++.


9
Треба сказати, що я ніколи не стикався з компілятором C ++, який генерує значно гірший код, ніж компілятор C для коду, написаного в спільному підмножині двох мов. Компілятори C ++ отримують поганий представник як щодо розміру, так і продуктивності, але мій досвід - це завжди неадекватне використання функцій C ++, які спричинили проблему ... Особливо, якщо ви стурбовані розміром, не використовуйте iostreams і не використовуйте шаблони, але в іншому випадку вам слід добре.
Жуль

@Jules: Тільки для того, що варто (не багато, IMO) Я бачив, що було продано як єдиний компілятор для C і C ++ (Turbo C ++ 1.0, якщо пам'ять служить) дає значно різні результати для однакового введення. Наскільки я розумію, однак це було до того, як вони закінчили свій власний компілятор C ++, тож хоч зовні він виглядав як один компілятор, у нього справді було два повністю окремих компілятора - один для C, другий для C ++.
Джеррі Труну

1
@JerryCoffin Якщо пам'ять служить, продукти Turbo ніколи не мали великої репутації. І якби це версія 1.0, ви можете попросити вибачення за те, що ви не були вдосконалені. Тож, мабуть, це не дуже репрезентативно.
Бармар

10
@Barmar: Насправді вони досить довго мали репутацію. Зараз їхня погана репутація пояснюється насамперед їхнім віком. Вони конкурують з іншими компіляторами тієї ж старовинні, але ніхто не публікує питань про те, як робити речі з gcc 1.4 (або будь-яким іншим). Але ти маєш рацію - це не дуже репрезентативно.
Джеррі Труну

1
@Jules Я заперечую, що навіть шаблони є нормальними. Незважаючи на популярну теорію, інстанціювання шаблону не збільшує неявно розмір коду, розмір коду, як правило, не збільшуватиметься, доки не буде використана функція з шаблону (у цьому випадку збільшення розміру буде пропорційним розміру функції) або якщо шаблон не буде декларування статичних змінних. Правила вбудовування все ще діють, тому можливо писати шаблони, де всі функції позначені компілятором.
Фарап

13

Ви ставите питання так, ніби компіляція з C ++ просто надала вам доступ до додаткових функцій. Це не так. Компіляція з компілятором C ++ означає, що ваш вихідний код інтерпретується як джерело C ++, а C ++ - мова, що відрізняється від C. У обох є загальний підмножина, достатньо великий, щоб корисно програмувати, але кожен має функції, яких не вистачає іншим, і це можна написати код, прийнятний для обох мов, але інтерпретується по-різному.

Якщо справді все, що ви хочете, - це перевантаження функцій, то я дійсно не бачу сенсу плутати проблему, вводячи в неї C ++. Замість різних функцій з однаковою назвою розрізняйте їхні списки параметрів, просто записуйте функції з різними іменами.

Що стосується ваших об'єктивних критеріїв,

  1. Чи значно зростає розмір виконуваного файлу, якщо ви використовуєте C ++.

Виконаний файл може бути трохи більшим при компіляції як C ++, відносно аналогічного коду С, побудованого як такого. Як мінімум, виконуваний файл C ++ матиме сумнівну перевагу керування іменами C ++ для всіх імен функцій, наскільки будь-які символи зберігаються у виконуваному файлі. (І це насправді, що передбачає ваші перевантаження в першу чергу.) Чи є різниця достатньо великою, щоб бути важливою для вас - це те, що вам доведеться визначити експериментом.

  1. Чи є помітний негативний вплив на продуктивність, якщо ви використовуєте C ++.

Я сумніваюся, ви побачите помітну різницю в характеристиці коду, який ви описуєте, порівняно з гіпотетичним аналогом чистого С.

  1. Чи може код використовувати повторно на іншій платформі, де доступний лише компілятор C.

Я б кинув на це дещо інше світло: якщо ви хочете пов’язати створений вами код з C ++ з іншим кодом, то або інший код також потрібно буде записати на C ++ і побудувати з C ++, або вам знадобиться створити спеціальне положення у власному коді (декларуючи зв'язок "C"), що, крім того, ви не можете зробити взагалі для своїх перевантажених функцій. З іншого боку, якщо ваш код написаний на C і складений як C, то інші можуть зв’язати його як з модулями C, так і з C ++. Таку проблему, як правило, можна подолати, обертаючи таблиці, але оскільки вам, здається, не потрібен C ++, то навіщо взагалі приймати таке питання?


4
"Виконаний файл може бути трохи більшим, коли компілюється як C ++ ... настільки, наскільки будь-які символи зберігаються у виконуваному файлі." Якщо справедливо, будь-який пристойний оптимізуючий ланцюжок інструментів повинен мати можливість зв’язування для позбавлення цих символів у "релізних" збірках, а це означає, що вони лише роздують ваші налагоджувальні збірки. Я не думаю, що це велика втрата. Насправді це частіше користь.
Коді Грей

5

Ще в той час використання компілятора C ++ як "кращого C" було рекламовано як випадок використання. Насправді, ранній C ++ був саме таким. Основний принцип дизайну полягав у тому, що ви можете використовувати лише ті функції, які ви хотіли, і не матимете витрат на функції, які ви не використовували. Отже, ви можете перевантажувати функції (заявляючи про наміри за допомогою overloadключового слова!), А решта вашого проекту не тільки склала б чудово, але і створила б код не гірший, ніж створив компілятор C.

Відтоді мови дещо розходилися.

Кожен mallocу своєму коді C буде помилка невідповідності типу - не проблема в вашому випадку, без динамічної пам'яті! Точно так само всі ваші voidпокажчики на C зірвуть вас, оскільки вам доведеться додати явні касти. Але ... чому ти це робиш саме так ... тебе будуть вести вниз все більше і більше використовувати функції C ++.

Тож, можливо, це можливо з додатковою роботою. Але це послужить шлюзом до прийняття більш широкого масштабу C ++. Через кілька років люди будуть скаржитися на ваш спадковий код, схожий на те, що він був написаний у 1989 році, коли вони замінюють ваші mallocдзвінки new, виривають блоки коду тіл циклу, щоб просто викликати алгоритм, а також лаяти небезпечний підроблений поліморфізм, який би були тривіальними, якби компілятору було дозволено це зробити.

З іншого боку, ви знаєте, що все-таки було б так само, якби ви написали це на C, тож коли-небудь неправильно писати на C замість C ++ з огляду на його існування? Якщо відповідь - «ні», то використання функцій зібраної вишні з C ++ також не може помилитися .


2

Багато мов програмування мають одну або кілька культур, що розвиваються навколо них, і мають конкретні уявлення про те, якою повинна бути "мова". Хоча це повинно бути можливим, практичним і корисним, щоб мова на низькому рівні підходила для системного програмування, яка збільшує діалект С, підходящий для такої мети, з деякими функціями C ++, які також були б придатними, культур, що оточують дві мови, не ' t особливо підтримує таке злиття.

Я читав про вбудовані компілятори C, які включали деякі функції C ++, включаючи можливість мати

foo.someFunction(a,b,c);

інтерпретується як, по суті

typeOfFoo_someFunction(foo, a, b, c);

а також можливість перевантажувати статичні функції (проблеми з управлінням іменами виникають лише при експортіфункції перевантажені). Немає причин, що компілятори C не повинні бути в змозі підтримувати таку функціональність, не пред'являючи додаткових вимог до середовища зв’язку та виконання, але багато рефлексивне відхилення компілятора C майже усього від C ++ в інтересах уникнення навантаження на навколишнє середовище змушує їх відхилити навіть функції, які б не нав'язували таких витрат. Тим часом, культура C ++ мало зацікавлена ​​в будь-якому середовищі, яке не може підтримувати всі функції цієї мови. Якщо культура C не зміниться на прийняття таких особливостей, або культура C ++ не зміниться, щоб заохотити реалізацію легких підмножин, "гібридний" код може зазнавати багатьох обмежень обох мов, обмежуючись його здатністю пожинати. переваги роботи в комбінованому суперсеті.

Якщо платформа підтримує і C, і C ++, додатковий код бібліотеки, необхідний для підтримки C ++, швидше за все зробить виконуваний файл дещо більшим, хоча ефект не буде особливо великим. Виконання "функцій C" в межах C ++, швидше за все, не вплине особливо. Більшою проблемою може бути те, як оптимізатори C та C ++ поводяться з різними кутовими справами. Поведінка типів даних у C здебільшого визначається вмістом бітів, що зберігаються в них, а C ++ має розпізнану категорію структур (PODS - Plain Old Data Structures), семантика якої також визначається в основному збереженими бітами. Однак і стандарти C і C ++ іноді допускають поведінку, що суперечить тій, що передбачається збереженими бітами, і винятки з поведінки "зберігаються біти" відрізняються між двома мовами.


1
Цікава думка, але не стосується питання ОП.
R Sahu

@RSahu: Код, який відповідає "культурі" навколо мови, може бути краще підтримується і легше адаптується до різних реалізацій, ніж код, який не відповідає. Культури C і C ++ протилежні тому типу використання, який пропонує ОП, я додам ще деякі конкретні моменти щодо конкретних питань.
supercat

1
Зауважте, що в стандартному комітеті C ++ є вбудована / фінансова / ігрова група, яка спрямована на вирішення саме тих ситуацій, що "мало цікавлять", на які ви заявляєте.
Якк

@Yakk: Зізнаюсь, я не слідкував за світом C ++ дуже уважно, оскільки мої інтереси в основному полягають у C; Я знаю, що були зроблені зусилля щодо більш вбудованих діалектів, але я не думав, що вони десь дістаються.
суперкарт

2

Ще один важливий фактор, який слід врахувати: хто успадкує ваш код.

Чи завжди ця людина буде програмістом на C із компілятором C? Я теж так думаю.

Чи буде ця людина також програмістом на C ++ із компілятором C ++? Я хотів би бути досить впевненим у цьому, перш ніж зробити свій код залежним від C ++ конкретних речей.


2

Поліморфізм - це дійсно хороша особливість, яку C ++ надає безкоштовно. Однак є й інші аспекти, які, можливо, потрібно буде враховувати при виборі компілятора. Існує альтернатива поліморфізму у С, але його використання може викликати деякі підняті брови. Його варіативну функцію див. У навчальному посібнику з варіатичною функцією .

У вашому випадку це було б щось на кшталт

enum serialDataTypes
{
 INT =0,
 INT_P =1,
 ....
 }Arg_type_t;
....
....
void transmit(Arg_type_t serial_data_type, ...)
{
  va_list args;
  va_start(args, serial_data_type);

  switch(serial_data_type)
  {
    case INT: 
    //Send integer
    break;

    case INT_P:
    //Send data at integer pointer
    break;
    ...
   }
 va_end(args);
}

Мені подобається підхід Філліпса, але він засмічує вашу бібліотеку безліччю дзвінків. З вищезазначеним інтерфейс чистий. У нього є свої недоліки і, зрештою, питання вибору.


Варіадні функції в основному обслуговують випадок використання, коли і кількість аргументів, і їх типи є змінними. В іншому випадку він вам не потрібен, і, зокрема, це надмірно для вашого конкретного прикладу. Було б простіше використовувати звичайну функцію, яка приймає Arg_type_tі void *вказує на дані. З урахуванням сказаного, так, надання (однієї) функції аргументу, який вказує на тип даних, дійсно є життєздатною альтернативою.
Джон Боллінгер

1

Для вашого конкретного випадку статичної перевантаження "функції" для кількох різних типів, ви можете замість цього просто використати C11 з його _Genericмакроапаратурою . Я думаю, що це може бути достатньо для ваших обмежених потреб.

Використовуючи відповідь Філіпа Кендала, ви можете визначити:

#define transmit(X) _Generic((X),      \
   unsigned char*: transmit_uchar_buffer, \
   char*: transmit_char_buffer,           \
   unsigned char: transmit_uchar,         \
   char: transmit_char) ((X))

і кодуйте transmit(foo) будь-який тип foo(серед чотирьох перерахованих вище типів).

Якщо ви дбаєте лише про компілятори GCC (і сумісні, наприклад, Clang ), ви можете розглянути __builtin_types_compatible_pпитання про її typeofрозширення.


1

Чи погана практика використовувати компілятор C ++ лише для перевантаження функцій?

Позиція ІМХО, так, і мені потрібно стати шизофреніком, щоб відповісти на цю, оскільки я люблю обидві мови, але це не має нічого спільного з ефективністю, а більше схоже на безпеку та ідіоматичне використання мов.

C сторона

З точки зору С, я вважаю, що це марно, щоб ваш код вимагав C ++ просто для використання перевантаження функцій. Якщо ви не використовуєте його для статичного поліморфізму з шаблонами C ++, це такий тривіальний синтаксичний цукор, отриманий в обмін на перехід на зовсім іншу мову. Крім того, якщо ви хочете експортувати свої функції в диліб (це може бути, а може, і не бути практичним питанням), ви вже не можете робити це дуже практично для широкого споживання з усіма символами, керованими іменами.

C ++ сторона

З точки зору C ++, ви не повинні використовувати C ++, як C, з функцією перевантаження. Це не стилістичний догматизм, а той, що стосується практичного використання повсякденного C ++.

Ваш звичайний вид C-коду є достатньо розумним і "безпечним" для запису, якщо ви працюєте проти системи типу C, яка забороняє такі речі, як копіювальні диски structs. Коли ви працюєте в набагато багатшій системі типу C ++, щоденні функції, які мають величезне значення, як memsetі memcpyне стають функціями, на які слід постійно спиратися. Натомість - це функції, яких ви, як правило, хочете уникати, як чума, оскільки з типами C ++ ви не повинні поводитися з ними як із сирими бітами та байтами, які потрібно копіювати та переміщувати навколо та звільняти. Навіть якщо ваш код використовує лише такі речі, як memsetпримітиви та POD UDT на даний момент, той момент, коли хтось додасть 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в контексті стилю С (що не означає, що 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 таким чином:

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 ++, як правило, схвалюють, оскільки функція не просочить жоден рядок коду, який викликає неявний вихід у результаті throw.

Виберіть мову

Вам слід або використовувати систему типів та філософію C ++ з RAII, виняток безпеки, шаблони, OOP тощо, або охоплювати C, яка значною мірою обертається навколо необроблених бітів і байтів. Ви не повинні укладати недобросовісний шлюб між цими двома мовами, а натомість розділяти їх на різні мови, щоб поводитись дуже по-різному, а не розмивати їх разом.

Ці мови хочуть вийти за вас заміж. Ви, як правило, повинні вибрати один замість того, щоб побачитись та обдурити обох. Або ви можете бути полігамістом, як я, і виходити заміж за обох, але вам доведеться повністю переключити своє мислення, проводячи час один над одним і тримати їх добре відокремленими один від одного, щоб вони не воювали один з одним.

Бінарний розмір

Щойно з цікавості я спробував взяти свою безкоштовну реалізацію списку та орієнтир зараз і перенести його на C ++, оскільки мені стало цікаво про це:

[...] не знаю, як це виглядатиме для C, оскільки я не використовував компілятор C.

... і хотів дізнатись, чи би бінарний розмір взагалі надувався лише таким чином, як C ++. Це вимагало від мене розсипати явні ролики по всьому місцю, яке було нечітко (одна з причин мені подобається насправді краще писати речі низького рівня, такі як розподільники та структури даних в С), але зайняла лише хвилина.

Це було лише порівнянням 64-розрядної версії MSVC для простого консольного додатка та з кодом, який не використовував жодних функцій C ++, навіть не перевантажуючи оператора - лише різниця між побудовою його на C та використанням, скажімо, <cstdlib>замість <stdlib.h>і такі речі, але я був здивований, виявивши, що це змінило нульовий розмір у бінарному розмірі!

Двійковий код був 9,728байтами, коли він був побудований в C, а також 9,278байтами, коли компілювався як код C ++. Я насправді цього не очікував. Я думав, що такі речі, як EH, принаймні трохи додадуть туди (думав, що це буде принаймні на сто байт), хоча, напевно, він міг зрозуміти, що не потрібно додавати інструкції, пов'язані з ЕГ, оскільки я просто використовуючи стандартну бібліотеку С і нічого не кидає. Я щось думавтак чи інакше додасть бінарний розмір, як RTTI. У всякому разі, це було якось круто, щоб це побачити. Звичайно, я не думаю, що ви повинні узагальнювати цей результат, але це мене принаймні трохи вразило. Це також не впливало на орієнтири, і, природно, так, оскільки я думаю, що однаковий отриманий бінарний розмір також означав ідентичні інструкції на машині.

Це означає, що хто піклується про двійковий розмір із вищезазначеними питаннями безпеки та техніки? Отже, знову ж таки, виберіть мову і сприйміть її філософію, а не намагайтеся її бастардити; ось що я рекомендую.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.