Тема: Поради зі стилю програмування на C++
http://uk.wikibooks.org/wiki/%D0%9D%D0% … 8F_C%2B%2B
Ласкаво прошу до обговорення, критики і коментарів.
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → C++ → Поради зі стилю програмування на C++
Для відправлення відповіді ви повинні увійти або зареєструватися
http://uk.wikibooks.org/wiki/%D0%9D%D0% … 8F_C%2B%2B
Ласкаво прошу до обговорення, критики і коментарів.
Мені сподобалася стаття, але хочеться більше почути ще про чисті ся. Особливо про ті нові божевільні можливості, що дозволяють за допомогою препроцесора перезавантажувати функції та ін.
40. До файлу заголовків треба додати захист включення.
Ця інформація вже застаріла, бо є
#pragma once
Усі мейнстріймові компілери підтримують цю опцію.
І напишіть ще про нічні жахи програмістів - мадярська нотація для найменувань змінних. І чому заголовкові файли вінди використовують чудернацькі типи даних? (Бо вона писалася до стандартизації сєй)
Мені сподобалася стаття, але хочеться більше почути ще про чисті ся. Особливо про ті нові божевільні можливості, що дозволяють за допомогою препроцесора перезавантажувати функції та ін.
Ви про _Generic? Так вам ніхто не заважає самостійно розкопати тему і створити гілку. Залюбки почитаю, може, й сам долучуся до обговорення. ISO/IEC 9899:2011 в поміч.
40. До файлу заголовків треба додати захист включення.
Ця інформація вже застаріла, бо є
#pragma once
Усі мейнстріймові компілери підтримують цю опцію.
Директива #pragma за стандартом залежить від реалізації. Тобто якщо це буде стандартизовано, то іншою директивою. До речі, ще існує #import... який також не стандартизований, але деінде використовується.
І напишіть ще про нічні жахи програмістів - мадярська нотація для найменувань змінних. І чому заголовкові файли вінди використовують чудернацькі типи даних? (Бо вона писалася до стандартизації сєй)
Цей текст не про криворуких програмерів, а про те, як самому не бути криворуким
Файна стаття, дякую.
Та кілька монеток вставлю:
9. Скорочення в іменах не треба записувати великими літерами [4].
Отут не погоджуся. Якщо код попаде до іншого програміста, є ризик, що він сплутає аббревіатуру зі скороченням всередині назви.
11. В приватні змінні класів слід додавати підкреслення наприкінці.
Якби стаття присвячувалася Пітону, я б зрозумів таке. Але який сенс на хрестах? Контроль доступу до полів визначається на етапі компіляції, і спроба прямого втручання у приват ззовні викличе бугурт компілятора.
void setDepth (int depth) { depth_ = depth; }
Якщо недоля спричинила нестачу уяви, то просто додайте води this.
17. Треба використовувати терміни get та set, коли забезпечується безпосередній доступ до атрибуту.-
Плюс у карму. Порушення цього положення викликало у мене певний ступор під час переходу від qt до fltk, а потім до wxWidgets.
18. Термін compute може використовуватися в методах, що щось обчислюють.
calcSmth, countSmth, processSmth - залежно, що саме рахує і яким чином.
Слід надавати перевагу американському initialize перед англійським initialise. Слід уникати скорочення init.
Знову я єретик-ортодокс, по ходу...
34. Файлам заголовків слід давати розширння .h (бажано) чи .hpp. Сирцевим файлам слід давати розширення .c++(рекомендовано), .C, .cc чи .cpp.
Поки я пишу під віндою, такий стиль (крім .c++ - лолват?) прийнятний. А під іксами ліпше брати .hxx, .cxx, які умовно покажуть, що програма написанна саме для іксових систем.
36. Реалізації слід повністю розміщувати в сирцевих файлах.
А як же inline-методи? Якщо метод описати прямо у заголовку, він стає inline автоматично, і якщо там щось просте типу setMyField(F* f){field = f;}, то який сенс засмічувати код сирця такими прищами?
38. Треба уникати особливих символів, таких, як TAB та розрив сторінки.
Згоден, крім табуляції: її розмір у пробілах дозволяють регулювати чи не всі нормальні редактори.
46. Змінні слід ініціалізувати при оголошенні.
Зайва операція - зайві проблеми, особливо якщо не є необхідною.
53. Неявну перевірку на 0 слід вживати тільки з булівськими змінними і вказівниками.
Знову падіння продуктивності. Не так уже й складно зрозуміти, що предикат у блоці if - це неявний bool. Плюс граблі, якщо забути "==" замість "="...
57. Можна уникати циклів do-while.
Імго, кожній конструкції - своя сфера використання.
58. Слід уникати break та continue.
Форма for(;;) не дуже читана, і не очевидно, що це дійсно нескінчений цикл.
о_О?
____
Якось так.
9. Скорочення в іменах не треба записувати великими літерами [4].
Отут не погоджуся. Якщо код попаде до іншого програміста, є ризик, що він сплутає аббревіатуру зі скороченням всередині назви.
Якщо є такий ризик - то треба користуватися п. 28
34. Файлам заголовків слід давати розширння .h (бажано) чи .hpp. Сирцевим файлам слід давати розширення .c++(рекомендовано), .C, .cc чи .cpp.
Поки я пишу під віндою, такий стиль (крім .c++ - лолват?) прийнятний. А під іксами ліпше брати .hxx, .cxx, які умовно покажуть, що програма написанна саме для іксових систем.
Як на мене, безглуздо вказувати розширенням цільову систему. Так ми до .hbsd і .cppwin6 дійдемо.
36. Реалізації слід повністю розміщувати в сирцевих файлах.
А як же inline-методи? Якщо метод описати прямо у заголовку, він стає inline автоматично, і якщо там щось просте типу setMyField(F* f){field = f;}, то який сенс засмічувати код сирця такими прищами?
Класика. Перший програміст пише очевидний інлайн-сеттер. Другий додає всередину count++. А третій довго шукає в файлі імплементації, чому це раптом змінюються дві змінні, якщо нічого не чіпалося.
38. Треба уникати особливих символів, таких, як TAB та розрив сторінки.
Згоден, крім табуляції: її розмір у пробілах дозволяють регулювати чи не всі нормальні редактори.
Так, про TAB - порада для досить специфічних ситуацій; але якщо пишете під консоль - краще не вживати і таби, невідомо, через який ssh доведеться його переглядати
46. Змінні слід ініціалізувати при оголошенні.
Зайва операція - зайві проблеми, особливо якщо не є необхідною.
Ну, там сказано, коли не треба. І взагалі п.54.
53. Неявну перевірку на 0 слід вживати тільки з булівськими змінними і вказівниками.
Знову падіння продуктивності. Не так уже й складно зрозуміти, що предикат у блоці if - це неявний bool. Плюс граблі, якщо забути "==" замість "="...
Вибачте, коли я народився, там ще було можливе зниження продуктивності, але коли вперше побачив комп'ютер, оптимізатори вже були розроблені і поширені. А якщо змінюється математика із зсувом з 0 на 1 - то проблеми точно будуть...
57. Можна уникати циклів do-while.
Імго, кожній конструкції - своя сфера використання.
Тому не слід, а можна.
60. Форма for(;;) не дуже читана, і не очевидно, що це дійсно нескінчений цикл.
о_О?
Ну, for(;;) можна перекласти як "для Ктулху" , що не означає нескінченості. А от "поки правда" - трохи краще.
Ну, for(;;) можна перекласти як "для Ктулху" , що не означає нескінченості. А от "поки правда" - трохи краще.
А якщо цикл написаний якраз особисто для Ктулху з метою створення якогось тайм-аута/затримки?
for(;;)
{
Sleep(n);
if( wakeUpDude())
break;
}
koala написав:Ну, for(;;) можна перекласти як "для Ктулху" , що не означає нескінченості. А от "поки правда" - трохи краще.
А якщо цикл написаний якраз особисто для Ктулху з метою створення якогось тайм-аута/затримки?
for(;;) { Sleep(n); if( wakeUpDude()) break; }
Нащо так просто? Давайте вже
void waitForWakeUp()
{
Sleep();
if(wakeUpDude())return;
else waitForWakeUp();
}
якщо вже легких шляхів не шукати...
Як на мене, безглуздо вказувати розширенням цільову систему. Так ми до .hbsd і .cppwin6 дійдемо.
Не дійдемо. Пригадую, що читав про загальноприйнятість .*xx назв для систем сімейства Unix серед цпп-розробників.
Класика. Перший програміст пише очевидний інлайн-сеттер. Другий додає всередину count++. А третій довго шукає в файлі імплементації, чому це раптом змінюються дві змінні, якщо нічого не чіпалося.
За таку класику халтуру треба молотком відбивати пальці. І Кент Бек буде першим у черзі на отримання молотка для цього.
Так, про TAB - порада для досить специфічних ситуацій; але якщо пишете під консоль - краще не вживати і таби, невідомо, через який ssh доведеться його переглядати
Ну і яка ймовірність того, що ви попадете на систему, де не буде навіть vim'а чи ще чогось езотеричнішого?
Вибачте, коли я народився, там ще було можливе зниження продуктивності, але коли вперше побачив комп'ютер, оптимізатори вже були розроблені і поширені. А якщо змінюється математика із зсувом з 0 на 1 - то проблеми точно будуть...
Довелося мені у недалекому минулому ваяти синтаксичний аналізатор текстових файлів. Мушу сказати, що поняття продуктивності нікуди не пропадало, особливо коли брався набір із порядка 1000 файлів по кількасот кб кожен.
Оптимізатор - теж дуже умовна річ. Можливо, програміст хотів ув одному моменті робити більше речей прямо у регістрах процесора, а оптимізатор вирішить по-своєму, бо є універсальним, а все універсальне - це лише матсподівання...
Bartash написав:57. Можна уникати циклів do-while.
Імго, кожній конструкції - своя сфера використання.Тому не слід, а можна.
Якось я пропустив період, коли do-while слід було використовувати повсюдно.
do { Sleep(n); }while(!wakeUpDude())
Best. Тут визнаю переваги do-while над for(;;) для даної задачі.
Класика. Перший програміст пише очевидний інлайн-сеттер. Другий додає всередину count++. А третій довго шукає в файлі імплементації, чому це раптом змінюються дві змінні, якщо нічого не чіпалося.
За таку класику халтуру треба молотком відбивати пальці. І Кент Бек буде першим у черзі на отримання молотка для цього.
Кому саме з трьох?
Ну і яка ймовірність того, що ви попадете на систему, де не буде навіть vim'а чи ще чогось езотеричнішого?
Поки що - відмінна від нуля. Хоча ймовірність ще й писати під таку систему, і налагоджувати безпосередньо на ній...
Оптимізатор - теж дуже умовна річ. Можливо, програміст хотів ув одному моменті робити більше речей прямо у регістрах процесора, а оптимізатор вирішить по-своєму, бо є універсальним, а все універсальне - це лише матсподівання...
Якщо програміст хоче працювати безпосередньо з процесором, то не користується C++.
Якось я пропустив період, коли do-while слід було використовувати повсюдно.
Ступінь важливості порад.
Поради зґруповані за темами і кожна порада пронумерована для полегшення посилань на них.
В цих настановах слова треба, слід та може мають особливе значення. Треба означає, що поради треба дотримуватися, слід - наполеглива рекомендація, а може - загальна настанова.
Це все ще нічого, я тут ось таке наперекладав (від того ж автора..): http://uk.wikibooks.org/wiki/%D0%9D%D0% … 8F_C%2B%2B
Стосовно оптимізації: спробуйте скопмілювати
clock_t start=clock();
int t=1;
for(int i=0;i<1000000;i++)t=t*2+1;
clock_t time1=clock()-start;
t=1;
for(int i=0;i<1000000;i++)t=(t<<1)|1;
clock_t time2=clock()-time1;
cout << "operation 1:" << time1 <<" operation 2:" <<time2;
і відпишіться по результатах...
Кому саме з трьох?
Другому. За інкремент у недозволених місцях.
Поки що - відмінна від нуля. Хоча ймовірність ще й писати під таку систему, і налагоджувати безпосередньо на ній...
От коли доведеться ваяти під OS 390 чи ще щось суворіше, то тоді ок. А поки що - шиндоус, нікси, цеброїди та яблука.
Якщо програміст хоче працювати безпосередньо з процесором, то не користується C++.
Приклад був про "пропозицію" register.
Це все ще нічого, я тут ось таке наперекладав (від того ж автора..):
То пізніше почитаю, бо флудозапас мій вичерпується потроху, рівно як і вільний час.
По коду:
Не хочете результат прокоментувати? Чому множення і додавання в рази швидші за зсув і побітове АБО?
Не хочете результат прокоментувати? Чому множення і додавання в рази швидші за зсув і побітове АБО?
Тут двояка тема - з одного боку дійсно воно швидше, бо використовуються команди shl/shr, а з іншого якщо компілятору так писати, то чи не леше принаймні частину проекта написати мовою асемблера і злінкувати усе разом або ж інлайн-ассемблером скористатись?
P.S.
Високорівневі кодери будуть гадати на кавовій гущи, а повний опис причини швидкості того коду можливо побачити лише порівнявши згенеровані асм лістинги декількох мейнстріймових компіляторів із різними опціями.
От у тому й проблема - ці дрібні "оптимізації" на кшталт заміни *2 на <<1 чи пропускання ==0 ніц не оптимізують (бо вбудований оптимізатор все одно зробить краще), а заплутати при читанні програми людиною можуть. Хочете реальної оптимізації на рівні машинних кодів - пишіть на асемблері, а пишете на мові високого рівня - залиште оптимізацію компілятору.
ці дрібні "оптимізації" на кшталт заміни *2 на <<1 чи пропускання ==0 ніц не оптимізують
Найдурніше те, що вони оптимізують, тому на джерельні коди С для 8051 процесора без сліз не глянеш. Хоча доки ви не майните біткойни/ламаєте хеш/створюєте 3д гру воно несуттєво впливає на результат. Принаймні VC++ 2010 прості як двері. Мабуть я створю статтю з порівнянням оптимізацій різних алгоритмів/різних процесорів, бо вмію реверс інженерити.
А можна не грозитися, а навести конкретний приклад? Я ж вам навів.
Приклад 1
Чому в дупу п'яний кодер, що знає лише одну двадцяту частину машинних команд, створює за один прохід асемблерний лістинг оптимальніший за багатопрохідні вумні компілятори С++ для процесорів архітектури х86?
Відповідь полягає в регістровій архітектурі х86 процесора. Найшвидше процесор робить операції саме із регістрами. Регістрів загального значення небагато (EAX, EBX, ECX, EDX, EDI, ESI) і на всі змінні їх не вистачає. До того ж регістри EDI, ESI, ECX використовуються для копіювання/порівняння, тому компілятори часто їх не використовують. Команда ділення/множення впливає на регістри EAX, EDX. Тому в більшості випадків компілятор зберігає змінні не у регістрах, а в пам'яті, іноді копіюючи їх у регістри для різних операцій і результат.. записує знову в па'мять. Тобто компіляторний лістинг буде повільнішим відсотків на 100% за асемблерний лістинг старого алконафта тільки через відсутність нормального розподілу регістрів. Людина має інтуїцію і вміє розкладувати пас'янcи краще за комп'ютер. Але це стосується лише CISC архітектури й х86 зокрема.
Стосовно оптимізації для 8051 архітектури напишу трохи згодом.
Давайте трохи визначимося із твердженнями, навколо яких іде суперечка. Я стверджую, що "звичайний" C/C++-код, опрацьований оптимізатором, в загальному випадку створює байт-код не гірший за "оптимізований" на рівні дрібних фокусів в програмі (на кшталт опускання !=0, заміни ...==0 на !..., заміни *4 на <<2 і т.д.), але значно краще читається. Що намагаєтеся довести ви? Що асемблер дає кращий код, ніж C++? Яким чином це стосується мого твердження?
Ну а "приклад" взагалі недоречний, бо його не можна відтворити. На відміну від мого.
Для відправлення відповіді ви повинні увійти або зареєструватися