Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

C++ Core Guidelines

7,664 views

Published on

C++ Core Guidelines talk for C++ Russia 2017, by Sergey Zubkov

Published in: Technology
  • Be the first to comment

  • Be the first to like this

C++ Core Guidelines

  1. 1. C++ Core Guidelines Сергей Зубков Morgan Stanley С благодарностями Бьярне Страуструпу
  2. 2. Zubkov - Guidelines - C++ Russia 2017 2 О чем это? (и почему волнуется Rust?)
  3. 3. Что такое "Современный C++"? • C++11, C++14, C++17, несколько технических спецификаций (концепции, интервалы, сеть, модули, корутины, и пр), множество новых возможностей и подходов. Правильный вопрос: • Как выглядит хороший современный С++? • Как он будет выглядеть в ближайшем будущем? • Как сделать его доступным рядовым программистам, а не экспертам из комитета? Zubkov - Guidelines - C++ Russia 2017 3
  4. 4. Что такое "Современный C++"? • Многие программисты (включая экспертов!) • Пишут в устаревшем стиле, игнорируя 20+ лет прогресса и опыта • Пишут в чужеродном стиле (как Java или как C) • Путаются в деталях • Наслаждаются деталями - Доктор, мне больно, когда я так делаю! - Так не делайте так! Zubkov - Guidelines - C++ Russia 2017 4
  5. 5. Что такое "Современный C++"? Как сделать современный C++ проще и доступнее? • прямо сейчас! • не создавая новый язык • сохраняя совместимость • cохраняя экспрессивную мощь • сохраняя производительность Три составные части нашего подхода: Набор правил + Библиотека + Статический анализ Zubkov - Guidelines - C++ Russia 2017 5
  6. 6. Набор правил + Библиотека + Статический анализ В каждой компании уже есть набор правил для C++! Типичные правила: • Создаются в начале проекта/компании • без опыта и наугад • Устаревают • обуза для пользователей / технический долг • Узко специализированы • применяются за пределами специализации • Недостаточно объяснены • требуют слепого подчинения или эксперта под рукой • Носят запретительный характер Zubkov - Guidelines - C++ Russia 2017 6
  7. 7. Набор правил + Библиотека + Статический анализ Чем отличаются Core Guidelines? • Современный С++ (и С++ ближайшего будущего) • Каким вы хотите видеть новый код через пять лет? • Предписания, не запрещения • Плюс разъяснения достаточные для использования в обучении • Не привязаны к конкретной компании • Гитхаб под эгидой isocpp.org • Анализаторы разрабатываются разными компаниями • Центральные (“Core”) • Компании и проекты могут включать и выключать группы правил (“профили”) и дописывать/удалять индивидуальные правила по вкусу – и это уже реальность. Zubkov - Guidelines - C++ Russia 2017 7
  8. 8. Набор правил + Библиотека + Статический анализ Чем отличаются Core Guidelines? Не только и не столько стиль программирования, в главные цели входят: • Статическая типобезопасность • Нет возможности обратиться к T как U • Безопасность работы с памятью • Нет доступа за пределы массива • Гарантии времени жизни всех объектов • Нет висячих указателей и ссылок • Ресурсобезопасность • Нет утечек ресурсов, включая утечки памяти • В будущем - больше Zubkov - Guidelines - C++ Russia 2017 8
  9. 9. Набор правил + Библиотека + Статический анализ Не цели • не минимальные • не ортогональные • не книга (и не учебник) • не руководство по модернизации кода (хотя есть правила модернизации) • не детальный список особых случаев (для этого есть стандарт) • не подмножество языка (не теряет экспрессивность) • не идеальные (сообщайте об ошибках) Zubkov - Guidelines - C++ Russia 2017 9
  10. 10. Набор правил + Библиотека + Статический анализ Правил много! • Не все программисты смогут включить все правила • Как минимум, не сразу: постепенная интеграция жизненно необходима • Многим потребуется больше правил • Для нетипичных ситуаций • Наша цель: центральные правила (Core Guidelines) • Правила для большинства • Переход количества в качество: • Нет утечкам ресурсов • Нет висячим указателям • Нет нарушениям типобезопасности Zubkov - Guidelines - C++ Russia 2017 10
  11. 11. Набор правил + Библиотека + Статический анализ “подмножество надмножества” (изначально разработан для доменно-специфичных языков на основе С++) • Нельзя запрещать без альтернатив • Альтернатива – стандартная библиотека • С дополнениями (потому что “прямо сейчас”) Guidelines Support Library (https://github.com/Microsoft/GSL) • Для необычных ситуаций – с собственными дополнениями (MyCompany Support Library) • Авторы библиотеки могут пользоваться грязными/сложными/опасными возможностями языка – для того они и существуют. Цель: безопасная работа с массивами (без дополнительных проверок!) Надмножество (библиотека): std::string_view, gsl::span, gsl::zstring Подмножество (правила): не превращать массивы в указатели не делать арифметику на указателях не передавать указатель и размер Zubkov - Guidelines - C++ Russia 2017 11 C++ GSL Нельзя: Стандартная библиотека Можно:
  12. 12. Набор правил + Библиотека + Статический анализ Microsoft Visual Studio LLVM Clang Некоторые другие, близкие по духу, статические анализаторы также планируют включить правила из Code Guidelines (например IFS Cevelop) Zubkov - Guidelines - C++ Russia 2017 12
  13. 13. Набор правил + Библиотека + Статический анализ Zubkov - Guidelines - C++ Russia 2017 13 Сколько ошибок в этой программе?
  14. 14. Набор правил + Библиотека + Статический анализ Zubkov - Guidelines - C++ Russia 2017 14
  15. 15. Набор правил + Библиотека + Статический анализ Zubkov - Guidelines - C++ Russia 2017 15
  16. 16. Набор правил + Библиотека + Статический анализ Zubkov - Guidelines - C++ Russia 2017 16
  17. 17. Набор правил github.com/isocpp/CppCoreGuidelines Zubkov - Guidelines - C++ Russia 2017 17
  18. 18. github.com/isocpp/CppCoreGuidelines • 2014-2015: Morgan Stanley + Microsoft + ЦЕРН • 13 сентября 2015 (CppCon15): на гитхабе Принимайте участие! Zubkov - Guidelines - C++ Russia 2017 18
  19. 19. Цели Core Guidelines • Сделать целые классы ошибок невозможными • Меньше времени в отладчике, меньше шансов для крахов и хаков • Сделать невозможными утечки ресурсов • Без потери производительности • Сделать невозможными висячие указатели • Без потери производительности • Сделать невозможным выход за границы массива • Без потери производительности • Упростить • Простой код проще поддерживать и проще преподавать • Подальше от темных мест и экзотических приемов • Программировать как эксперты Zubkov - Guidelines - C++ Russia 2017 19
  20. 20. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Zubkov - Guidelines - C++ Russia 2017 20
  21. 21. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Смысл кода должен быть понятен из кода: комментарии не компилируются! change_speed(double s); // Что за double? change_speed(2.3); // В каких единицах? change_speed(Speed s); // ok: это новая скорость change_speed(23_m / 10s); // ok: метры в секунду change_speed(2.3); // ошибка компиляции Zubkov - Guidelines - C++ Russia 2017 21
  22. 22. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Пишите на стандартном С++, запрещайте нестандартные расширения языка. Когда они действительно нужны, изолируйте от остального кода. Для gcc и clang “-std=c++14” недостаточно! Расширения отключаются ”-pedantic-errors” Zubkov - Guidelines - C++ Russia 2017 22
  23. 23. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Код должен выражать цели, не средства Плохо: int i = 0; while (i < v.size()) { /* работа с v[i] */ } Лучше: for (const auto& x : v) { /* работа с x */ } // порядок важен for_each(par, v, [](int x) { /* работа с x */ }); // порядок неважен Zubkov - Guidelines - C++ Russia 2017 23
  24. 24. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Статическая типобезопасность (см также type safety profile) std::variant вместо union Нет преобразований массивов в указатели (см gsl::span) Нет сужающих конверсий (см gsl::narrow_cast) Нет кастов (явных приведений типов) Никаких void* Zubkov - Guidelines - C++ Russia 2017 24
  25. 25. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Предпочитайте ошибки компиляции проверкам в коде static_assert(sizeof(void*)==8, “64 bit required”); static_assert(sizeof(VMPage) == PAGESIZE); f(gsl::span<int>); // а не f(int*, int) Zubkov - Guidelines - C++ Russia 2017 25
  26. 26. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Если нельзя сделать ошибкой компиляции, должна быть возможность проверить в коде. Контракты (см gsl_assert) Исключения Валидность указателя проверить невозможно!Zubkov - Guidelines - C++ Russia 2017 26
  27. 27. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Проверяйте ошибки как можно раньше (идеально в конструкторах, чтобы не повторять проверки) std::vector и gsl::span вместо массивов gsl::not_null вместо указателей Структуры и классы вместо строк Zubkov - Guidelines - C++ Russia 2017 27
  28. 28. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Никаких утечек ресурсов – памяти, файлов, и т п. RAII классы (std::vector, std::fstream, std::lock_guard) std::make_unique/std::make_shared gsl::finally Никаких malloc/free/new/delete/strdup/fopen Zubkov - Guidelines - C++ Russia 2017 28
  29. 29. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Эффективно используйте и память и процессор: это все-таки C++! Всегда планируйте как минимум возможность оптимизации (Per.7). Избегайте лишнего кода (знайте, что есть в библиотеках), лишних проверок (см P.7 и правила об инвариантах), избыточные структуры данных (от ненужных связных списков до плохо упакованных структур). Zubkov - Guidelines - C++ Russia 2017 29
  30. 30. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Предпочитайте константы и неизменяемые данные Все что может быть constexpr или const, должно быть: объекты, методы, ссылки и указатели. (исключения – объекты, возвращаемые из функций) Zubkov - Guidelines - C++ Russia 2017 30
  31. 31. Секция P: Философия • P.1: Express ideas directly in code • P.2: Write in ISO Standard C++ • P.3: Express intent • P.4: Ideally, a program should be statically type safe • P.5: Prefer compile-time checking to run-time checking • P.6: What cannot be checked at compile time should be checkable at run time • P.7: Catch run-time errors early • P.8: Don't leak any resources • P.9: Don't waste time or space • P.10: Prefer immutable data to mutable data • P.11: Encapsulate messy constructs, rather than spreading through the code Прячьте грязный код в библиотеках за удобными интерфейсами, и больше им не пользуйтесь. Это опять принцип “подмножество надмножества” в стандартной библиотеке масса примеров: union внутри std::variant, указатели внутри std::vector и пр Zubkov - Guidelines - C++ Russia 2017 31
  32. 32. Интерфейсы (фрагмент) • I.1: Make interfaces explicit • I.2: Avoid global variables • I.3: Avoid singletons • I.4: Make interfaces precisely and strongly typed • I.5: State preconditions (if any) • I.7: State postconditions • I.10: Use exceptions to signal a failure to perform a required task • I.11: Never transfer ownership by a raw pointer (T*) • I.12: Declare a pointer that must not be null as not_null • I.13: Do not pass an array as a single pointer • I.24: Avoid adjacent unrelated parameters of the same type • I.26: If you want a cross-compiler ABI, use a C-style subset Zubkov - Guidelines - C++ Russia 2017 32
  33. 33. Интерфейсы (фрагмент) • I.1: Make interfaces explicit • I.2: Avoid global variables • I.3: Avoid singletons • I.4: Make interfaces precisely and strongly typed • I.5: State preconditions (if any) • I.7: State postconditions • I.10: Use exceptions to signal a failure to perform a required task • I.11: Never transfer ownership by a raw pointer (T*) • I.12: Declare a pointer that must not be null as not_null • I.13: Do not pass an array as a single pointer • I.24: Avoid adjacent unrelated parameters of the same type • I.26: If you want a cross-compiler ABI, use a C-style subset Zubkov - Guidelines - C++ Russia 2017 33 Передача массива в виде одного указателя и даже в виде пары аргументов (указатель и размер, указатели на начало и конец) – источник ошибок. Размер массива не должен быть отделен от самого массива. Плохо: void copy_n(const T* p, T* q, int n); // копия из [p:p+n) в [q:q+n) Лучше: void copy(gsl::span<const T> r, gsl::span<T> r2); // копия из r в r2
  34. 34. Интерфейсы (фрагмент) • I.1: Make interfaces explicit • I.2: Avoid global variables • I.3: Avoid singletons • I.4: Make interfaces precisely and strongly typed • I.5: State preconditions (if any) • I.7: State postconditions • I.10: Use exceptions to signal a failure to perform a required task • I.11: Never transfer ownership by a raw pointer (T*) • I.12: Declare a pointer that must not be null as not_null • I.13: Do not pass an array as a single pointer • I.24: Avoid adjacent unrelated parameters of the same type • I.26: If you want a cross-compiler ABI, use a C-style subset Zubkov - Guidelines - C++ Russia 2017 34 Функции сообщают от ошибках исключениями (“ошибка” – невозможность выполнить свою функцию, гарантировать постусловие или восстановить инвариант). int printf(const char* ...); // Плохо: возвращает код ошибки template <class F, class ...Args> explicit thread(F&& f, Args&&... args); // Лучше: бросает system_error
  35. 35. Функции (фрагмент) Zubkov - Guidelines - C++ Russia 2017 35 • F.22: Use T* or owner<T*> or a smart pointer to designate a single object • F.23: Use a not_null<T> to indicate "null" is not a valid value • F.24: Use a span<T> or a span_p<T> to designate a half-open sequence • F.25: Use a zstring or a not_null<zstring> to designate a C-style string • F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed • F.27: Use a shared_ptr<T> to share ownership • F.44: Return a T& when copy is undesirable and "returning no object" isn't an option • F.45: Don't return a T&& • F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function) • F.51: Where there is a choice, prefer default arguments over overloading • F.52: Prefer capturing by reference in lambdas that will be used locally
  36. 36. Функции (фрагмент) Zubkov - Guidelines - C++ Russia 2017 36 • F.22: Use T* or owner<T*> or a smart pointer to designate a single object • F.23: Use a not_null<T> to indicate "null" is not a valid value • F.24: Use a span<T> or a span_p<T> to designate a half-open sequence • F.25: Use a zstring or a not_null<zstring> to designate a C-style string • F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed • F.27: Use a shared_ptr<T> to share ownership • F.44: Return a T& when copy is undesirable and "returning no object" isn't an option • F.45: Don't return a T&& • F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function) • F.51: Where there is a choice, prefer default arguments over overloading • F.52: Prefer capturing by reference in lambdas that will be used locally • gsl::not_null<T> (где T = U*, std::unique_ptr<U>, std::shared_ptr<U>), гарантирует что T - не нулевой указатель. (см также I.12) • Если параметр not_null, проверка в вызываемой функции не нужна • Eсли результат not_null, проверка в вызывающей функции не нужна int f(const int* p, gsl::not_null<const int*> q) { if(q) return *q; // бессмысленная проверка (F.23) return *p; // опасный код! Голый указатель может быть нулевым (F.60) }
  37. 37. Функции (фрагмент) Zubkov - Guidelines - C++ Russia 2017 37 • F.22: Use T* or owner<T*> or a smart pointer to designate a single object • F.23: Use a not_null<T> to indicate "null" is not a valid value • F.24: Use a span<T> or a span_p<T> to designate a half-open sequence • F.25: Use a zstring or a not_null<zstring> to designate a C-style string • F.26: Use a unique_ptr<T> to transfer ownership where a pointer is needed • F.27: Use a shared_ptr<T> to share ownership • F.44: Return a T& when copy is undesirable and "returning no object" isn't an option • F.45: Don't return a T&& • F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function) • F.51: Where there is a choice, prefer default arguments over overloading • F.52: Prefer capturing by reference in lambdas that will be used locally • Если есть возможность, пользуйтеся аргументами по умолчанию вместо перегрузки функций: перегрузка – для похожих операций над аргументами разных типов. • Плохо: нет гарантии что код этих двух функций идентичен void print(const string& s); // формат по умолчанию void print(const string& s, format f); • Лучше: void print(const string& s, format f = {});
  38. 38. Параметры и аргументы (фрагмент) Zubkov - Guidelines - C++ Russia 2017 38
  39. 39. Классы (фрагмент) Zubkov - Guidelines - C++ Russia 2017 39 • C.2: Use class if the class has an invariant; use struct if the data members can vary independently • C.4: Make a function a member only if it needs direct access to the representation of a class • C.5: Place helper functions in the same namespace as the class they support • C.20: If you can avoid defining any default operations, do • C.21: If you define or =delete any default operation, define or =delete them all • C.35: A base class with a virtual function needs a virtual destructor • C.36: A destructor may not fail • C.40: Define a constructor if a class has an invariant • C.41: A constructor should create a fully initialized object • C.42: If a constructor cannot construct a valid object, throw an exception • C.43: Ensure that a class has a default constructor • C.45: Prefer to use member initializers
  40. 40. Классы (фрагмент) Zubkov - Guidelines - C++ Russia 2017 40 • C.2: Use class if the class has an invariant; use struct if the data members can vary independently • C.4: Make a function a member only if it needs direct access to the representation of a class • C.5: Place helper functions in the same namespace as the class they support • C.20: If you can avoid defining any default operations, do • C.21: If you define or =delete any default operation, define or =delete them all • C.35: A base class with a virtual function needs a virtual destructor • C.36: A destructor may not fail • C.40: Define a constructor if a class has an invariant • C.41: A constructor should create a fully initialized object • C.42: If a constructor cannot construct a valid object, throw an exception • C.43: Ensure that a class has a default constructor • C.45: Prefer to use member initializers • Конструктор устанавливает инвариант, функции-члены считают, что инвариант гарантирован. • Если данные можно менять напрямую, инвариант невозможен. • struct Desc { string left; // left и right могут меняться независимо друг от друга string right; // инварианта нет }; • class IntArray { public: // конструкторы, деструктор и пр private: gsl::owner<int*> beg; // инвариант: beg указывает на массив int* end; // инвариант: end указывает на конец массива };
  41. 41. Классы (фрагмент) Zubkov - Guidelines - C++ Russia 2017 41 • C.2: Use class if the class has an invariant; use struct if the data members can vary independently • C.4: Make a function a member only if it needs direct access to the representation of a class • C.5: Place helper functions in the same namespace as the class they support • C.20: If you can avoid defining any default operations, do • C.21: If you define or =delete any default operation, define or =delete them all • C.35: A base class with a virtual function needs a virtual destructor • C.36: A destructor may not fail • C.40: Define a constructor if a class has an invariant • C.41: A constructor should create a fully initialized object • C.42: If a constructor cannot construct a valid object, throw an exception • C.43: Ensure that a class has a default constructor • C.45: Prefer to use member initializers • Стандартные конструкторы • Делают возможным массивы и упрощают код • Требуются для все регулярных типов • Предоставляют состояние, которое можно использовать в операциях перемещения • class Date { public: Date(Day dd, Month mm, Year yyyy); Date() = default; // См C.45 private: int day = 1; int month = 1; int year = 1970; }; std::vector<Date> vd(1000);
  42. 42. Наследование (фрагмент) • C.121: If a base class is used as an interface, make it a pure abstract class • C.126: An abstract class typically doesn't need a constructor • C.127: A class with a virtual function should have a virtual or protected destructor • C.128: Virtual functions should specify exactly one of virtual, override, or final • C.131: Avoid trivial getters and setters • C.132: Don't make a function virtual without reason • C.133: Avoid protected data • C.138: Create an overload set for a derived class and its bases with using • C.140: Do not provide different default arguments for a virtual function and an overrider • C.146: Use dynamic_cast where class hierarchy navigation is unavoidable • C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an error • C.152: Never assign a pointer to an array of derived class objects to a pointer to its base Zubkov - Guidelines - C++ Russia 2017 42
  43. 43. Наследование (фрагмент) • C.121: If a base class is used as an interface, make it a pure abstract class • C.126: An abstract class typically doesn't need a constructor • C.127: A class with a virtual function should have a virtual or protected destructor • C.128: Virtual functions should specify exactly one of virtual, override, or final • C.131: Avoid trivial getters and setters • C.132: Don't make a function virtual without reason • C.133: Avoid protected data • C.138: Create an overload set for a derived class and its bases with using • C.140: Do not provide different default arguments for a virtual function and an overrider • C.146: Use dynamic_cast where class hierarchy navigation is unavoidable • C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an error • C.152: Never assign a pointer to an array of derived class objects to a pointer to its base Zubkov - Guidelines - C++ Russia 2017 43 • Если в классе есть виртуальная функция, деструктор должет быть виртуальными и доступным или невиртуальным и защищенным • (удаление полиморфного класса с невиртуальным деструктором через базовый указатель – неопределенное поведение) • struct B { virtual int f() = 0; // деструктор по умолчанию доступный и невиртуальный }; struct D : B { string s {"default"}; }; void use() { std::unique_ptr<B> p = std::make_unique<D>(); } // Undefined Behavior (как минимум утечка памяти из-за строки)
  44. 44. Наследование (фрагмент) • C.121: If a base class is used as an interface, make it a pure abstract class • C.126: An abstract class typically doesn't need a constructor • C.127: A class with a virtual function should have a virtual or protected destructor • C.128: Virtual functions should specify exactly one of virtual, override, or final • C.131: Avoid trivial getters and setters • C.132: Don't make a function virtual without reason • C.133: Avoid protected data • C.138: Create an overload set for a derived class and its bases with using • C.140: Do not provide different default arguments for a virtual function and an overrider • C.146: Use dynamic_cast where class hierarchy navigation is unavoidable • C.147: Use dynamic_cast to a reference type when failure to find the required class is considered an error • C.152: Never assign a pointer to an array of derived class objects to a pointer to its base Zubkov - Guidelines - C++ Russia 2017 44 • Тривиальные геттеры и сеттеры не нужны • // Плохо: • class Point { int x; public: int get_x() const { return x; } void set_x(int xx) { x = xx; } }; • // Лучше • struct Point { int x = 0; }
  45. 45. Шаблоны (фрагмент) • T.5: Combine generic and OO techniques to amplify their strengths, not their costs • T.61+T.62: Don’t over-parametrize • T.64: Use specialization to provide alternative implementations of class templates • T.65: Use tag dispatch to provide alternative implementations of functions • T.67: Use specialization to provide alternative implementations for irregular types • T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point • #749 avoid recursion in variadic templates • T.122: Use templates (usually template aliases) to compute types at compile time • T.123: Use constexpr functions to compute values at compile time • T.144: Don't specialize function templates Zubkov - Guidelines - C++ Russia 2017 45
  46. 46. Шаблоны (фрагмент) • T.5: Combine generic and OO techniques to amplify their strengths, not their costs • T.61+T.62: Don’t over-parametrize • T.64: Use specialization to provide alternative implementations of class templates • T.65: Use tag dispatch to provide alternative implementations of functions • T.67: Use specialization to provide alternative implementations for irregular types • T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point • #749 avoid recursion in variadic templates • T.122: Use templates (usually template aliases) to compute types at compile time • T.123: Use constexpr functions to compute values at compile time • T.144: Don't specialize function templates Zubkov - Guidelines - C++ Russia 2017 46 • Излишняя параметризация – источник раздувания кода (даже если компилятор поддерживает ICF). • Если член шаблона зависит только от N параметров, он должен быть в базовом классе, зависящем только от этих N параметров. • // Плохо: • template<typename T> class Foo { enum { v1, v2 }; … }; • // Лучше: • struct Foo_base { enum { v1, v2 }; … }; template<typename T> class Foo : public Foo_base { … };
  47. 47. Шаблоны (фрагмент) • T.5: Combine generic and OO techniques to amplify their strengths, not their costs • T.61+T.62: Don’t over-parametrize • T.64: Use specialization to provide alternative implementations of class templates • T.65: Use tag dispatch to provide alternative implementations of functions • T.67: Use specialization to provide alternative implementations for irregular types • T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point • #749 avoid recursion in variadic templates • T.122: Use templates (usually template aliases) to compute types at compile time • T.123: Use constexpr functions to compute values at compile time • T.144: Don't specialize function templates Zubkov - Guidelines - C++ Russia 2017 47 • Рекурсия в шаблонном коде тормозит компиляцию. Ее можно избежать при помощи fold expressions, constexpr функций, std::index_sequence (std::invoke и т п) и просто pack expansions. • // Плохо: • template<class T> void pbv(vector<T>& v) {} template<class T, class H, class... Ts> void pbv(vector<T>& v, H&& h, Ts&&... Ts) { v.push_back(h); pbv(v, ts...); } • // Лучше template<class T, class... Ts) void pbv(vector<T>& v, Ts&&... Ts) { (v.push_back(ts), ...); }
  48. 48. Профили • Профиль – набор правил ставящих целью обеспечить конкретную гарантию. • Type safety (типобезопасность) • Bounds safety (безопасность работы с массивами) • Lifetime safety (гарантии времени жизни объектов) • В будущем – arithmetic, concurrency, noexcept, noalloc, etc. Zubkov - Guidelines - C++ Russia 2017 48
  49. 49. Профиль типобезопасности • P.4: Ideally, a program should be statically type safe • C.183: Don't use a union for type punning • Type.1: Don't use reinterpret_cast • Type.2: Don't use static_cast downcasts. Use dynamic_cast instead • Type.3: Don't use const_cast to cast away const (i.e., at all) • Type.4: Don't use C-style (T)expression casts that would perform a static_cast downcast, const_cast, or reinterpret_cast • Type.5: Don't use a variable before it has been initialized • Type.6: Always initialize a member variable Zubkov - Guidelines - C++ Russia 2017 49
  50. 50. Массивы и Указатели (Bounds Profile) • ES.55: Avoid the need for range checking • Bounds.1: Don't use pointer arithmetic. Use span instead. • Bounds.2: Only index into arrays using constant expressions. • Bounds.3: No array-to-pointer decay. • Bounds.4: Don't use standard library functions and types that are not bounds-checked. Zubkov - Guidelines - C++ Russia 2017 50
  51. 51. Контроль времени жизни объектов (Lifetime Profile) 1. У каждого объекта есть только один владелец 2. На объект могут ссылаться много ссылок/указателей 3. Никакая ссылка/указатель не может пережить владельца • Правила для обычной функции: • Не показывать ссылку/указатель на локальную переменную тому кто вызвал • Ссылка/указатель переданная как аргумент может быть возвращена • Указатель от new может быть возвращен если мы сообщим что этот указатель – владелец int* f(int* p) { int x = 4; return &x; // Ошибка return new int{7}; // OK, только надо отметить что это - владелец return p; // OK: откуда пришло, туда и вернется } Zubkov - Guidelines - C++ Russia 2017 51
  52. 52. Контроль времени жизни объектов (Lifetime Profile) • Как отмечать владельцев? • Предпочтительно: std::unique_ptr, std::vector, std::map, std::shared_ptr, • На низком уровне (например внутри std::vector<T>): gsl::owner<T> template<class T> using owner = T; • Этот низкоуровневый owner существует только на уровне системы типов и статического анализа, в компилированном коде он неотличим от T vector<int*> f(int* p) { owner<int*> q = new int{7}; vector<int*> res = {p, q}; // OK: копия q – не владелец return res; // Ошибка: владелец должен быть возвращен или удален } Zubkov - Guidelines - C++ Russia 2017 52
  53. 53. Правила будущего • T.10/I.9: Specify concepts for all template arguments • T.11: Whenever possible use standard concepts • T.12: Prefer concept names over auto for local variables • T.13: Prefer the shorthand notation for simple, single-type argument concepts • T.20: Avoid "concepts" without meaningful semantics • T.21: Require a complete set of operations for a concept • T.22: Specify axioms for concepts • T.23: Differentiate a refined concept from its more general case by adding new use patterns • T.24: Use tag classes or traits to differentiate concepts that differ only in semantics • T.25: Avoid complementary constraints • T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax • T.30+31: Use !C<T> / C1<T> || C2<T> sparingly • I.6+8: Prefer Expects() for expressing preconditions/ Ensures() for expressing postconditions Zubkov - Guidelines - C++ Russia 2017 53 См также http://stroustrup.com/good_concepts.pdf
  54. 54. Не-правила и мифы • NR.1: Don't: All declarations should be at the top of a function • NR.2: Don't: Have only a single return-statement in a function • NR.3: Don't: Don't use exceptions • NR.4: Don't: Place each class declaration in its own source file • NR.5: Don't: Don't do substantive work in a constructor; instead use two-phase initialization • NR.6: Don't: Place all cleanup actions at the end of a function and goto exit • NR.7: Don't: Make all data members protected Zubkov - Guidelines - C++ Russia 2017 54
  55. 55. Слишком много правил? • Эти правила для всех: • Новичков, экспертов, библиотек, системщиков, обычных и необычных приложений • Никто не предлагает выучить все эти правила! • Никто не предлагает даже прочитать все эти правила! • Подавляющее большинство правил предназначено для анализаторов: • Анализ отметит возможное нарушение и покажет какое правило прочитать: • Zubkov - Guidelines - C++ Russia 2017 55
  56. 56. Мы хотим хороший современный С++ • “Мы” значит “вы” • это не под силу одному человеку или одной компании • Каким вы хотите видеть код через 5 лет? • “Таким же как и сегодня” – не ответ • Модернизировать много кода – нелегко • Если ясна цель – можно к ней двигаться • Не все согласны с тем как выглядит хороший С++ • Код не должен выглядеть одинакого • Но должно быть общее ядро • Нужно обсуждать и применять правила и анализаторы • Помогайте! • Нужны правила, анализ, комментарии, редакторы https://github.com/isocpp/CppCoreGuidelines Zubkov - Guidelines - C++ Russia 2017 56
  57. 57. Zubkov - Guidelines - C++ Russia 2017 57
  58. 58. extras Zubkov - Guidelines - C++ Russia 2017 58
  59. 59. Обработка ошибок (фрагмент) • E.1: Develop an error-handling strategy early in a design • E.2: Throw an exception to signal that a function can't perform its assigned task • E.3: Use exceptions for error handling only • E.5: Let a constructor establish an invariant, and throw if it cannot • E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable • E.13: Never throw while being the direct owner of an object • E.14: Use purpose-designed user-defined types as exceptions (not built-in types) • E.15: Catch exceptions from a hierarchy by reference • E.16: Destructors, deallocation, and swap must never fail • E.17: Don't try to catch every exception in every function • E.18: Minimize the use of explicit try/catch • E.19: Use a final_action object to express cleanup if no suitable resource handle is available Zubkov - Guidelines - C++ Russia 2017 59
  60. 60. Многозадачность (избранное) • CP.4: Think in terms of tasks, rather than threads • CP.8: Don't try to use volatile for synchronization • CP.20+21: Use RAII, never plain lock()/unlock() / Use std::lock() to acquire multiple mutexes • CP.22: Never call unknown code while holding a lock (e.g., a callback) • CP.23+24: Think of a joining thread as a scoped container / a detached thread as a global container • CP.25+26: Prefer gsl::raii_thread / gsl::detached_thread • CP.31: Pass small amounts of data between threads by value, rather than by reference or pointer • CP.32: To share ownership between unrelated threads use shared_ptr • CP.42: Don't wait without a condition • CP.50: Define a mutex together with the data it protects • Use algorithms that are designed for parallelism, not algorithms with unnecessary dependency on linear evaluation • CP.60: Use a future to return a value from a concurrent task • CP.100: Don't use lock-free programming unless you absolutely have to Zubkov - Guidelines - C++ Russia 2017 60
  61. 61. Управление ресурсами • R.1: Manage resources automatically using resource handles and RAII • R.3: A raw pointer (a T*) is non-owning • R.4: A raw reference (a T&) is non-owning • R.5: Prefer scoped objects • R.10+11: Avoid malloc() and free() / Avoid calling new and delete explicitly • R.12: Immediately give the result of an explicit resource allocation to a manager object • R.13: Perform at most one explicit resource allocation in a single expression statement • R.15: Always overload matched allocation/deallocation pairs • R.20: Use unique_ptr or shared_ptr to represent ownership • R.21: Prefer unique_ptr over shared_ptr unless you need to share ownership • R.22+23: Use make_shared() to make shared_ptrs / make_unique() to make unique_ptrs • R.30: Take smart pointers as parameters only to explicitly express lifetime semantics Zubkov - Guidelines - C++ Russia 2017 61
  62. 62. Выражения и инструкции (избранное) • ES.5: Keep scopes small • ES.11: Use auto to avoid redundant repetition of type names • ES.20: Always initialize an object • ES.21+22: Don't introduce a variable (or constant) before you need to use it / until you have a value to initialize it with • ES.23: Prefer the {}-initializer syntax • ES.24: Use a unique_ptr<T> to hold pointers in code that may throw • ES.26: Don't use a variable for two unrelated purposes • ES.28: Use lambdas for complex initialization, especially of const variables • ES.30+31: Don't use macros for program text manipulation / for constants or "functions" • ES.45: Avoid narrowing conversions • ES.48: Avoid casts • ES.56: Avoid std::move() in application code • ES.63: Don't slice Zubkov - Guidelines - C++ Russia 2017 62
  63. 63. const • P.10: Prefer immutable data to mutable data • ES.25: Declare an object const or constexpr unless you want to modify its value later on • ES.50: Don't cast away const • F.4: If a function may have to be evaluated at compile time, declare it constexpr • R.6: Avoid non-const global variables • Con.1: By default, make objects immutable • Con.2: By default, make member functions const • Con.3: By default, pass pointers and references to consts • Con.4: Use const to define objects with values that do not change after construction • Con.5: Use constexpr for values that can be computed at compile time Zubkov - Guidelines - C++ Russia 2017 63

×