-
Notifications
You must be signed in to change notification settings - Fork 45
cpp-notes fixes #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
cpp-notes fixes #38
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -451,8 +451,8 @@ base2& to_base2(derived& d) { | |||||
| При этом, понятно, кастовать `void*` куда-то корректно можно, если там изначально было то, куда вы кастуете. | ||||||
|
|
||||||
| Аналогично, *вниз по иерархии* (от базового к наследуемому) можно кастовать только тогда, когда совпадает динамический тип. Иначе UB. | ||||||
| - `reinterpret_cast` — всё зашкварное из C-style cast'а. Перевод указателей несвязанных друг с другом типов, указателей в число и обратно. Простой и эффективный способ получить UB. В стандарте так и написано, это implementation-defined cast. Обратитесь к поставщику вашего компилятора, чтобы понять, как у вас работает `reinterpret_cast`. | ||||||
| - `const_cast`. Снимает модификаторы `const` и `volatile`. Чаще всего это делать не надо, но иногда бывает нужно всё-таки. В стародавние времена, когда `const`'ов не было, были функции, принимавшие указатель. Неконстантный, хотя не меняли его содержимое. И вот если вы хотите использовать эту функцию, вы можете снять `const` с указателя. А вообще правило вот какое: **если изначальный объект был `const`, снимать с него `const` ни в коем случае нельзя (UB). Если изначальный объект константным не был, а потом вы сначала навесили `const`, а потом сняли, то всё хорошо**. | ||||||
| - `reinterpret_cast` — всё зашкварное из C-style cast'а. Перевод указателей несвязанных друг с другом типов, указателей в число и обратно. Простой и эффективный способ получить UB, так как с помощью `reintepret_cast` очень просто нарушить `strict aliasing rule`. В стандарте так и написано, это implementation-defined cast. Обратитесь к поставщику вашего компилятора, чтобы понять, как у вас работает `reinterpret_cast`. Но как правило `reintepret_cast<T2*>(T1*)` работает как `static_cast<T2*>(static_cast<void*>(T1*))`. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. почему strict aliasing rule отформатирован как код?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. после "Но как правило" должна быть запятая (если это выражение останется после изменений) |
||||||
| - `const_cast`. Снимает модификаторы `const` и `volatile` (с помощью `const_cast` можно также навешивать `const`, но это не имеет особого смысла). Чаще всего это делать не надо, но иногда бывает нужно всё-таки. В стародавние времена, когда `const`'ов не было, были функции, принимавшие указатель. Неконстантный, хотя не меняли его содержимое. И вот если вы хотите использовать эту функцию, вы можете снять `const` с указателя. А вообще правило вот какое: **если изначальный объект был `const`, снимать с него `const` ни в коем случае нельзя (UB). Если изначальный объект константным не был, а потом вы сначала навесили `const`, а потом сняли, то всё хорошо**. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. дописанное в скобках относится не только к
Suggested change
|
||||||
| - `dynamic_cast`. Немного другое, нежели все остальные касты. Работает для указателей и ссылок **на полиморфные (хотя бы одна виртуальная функция) типы**. Кастует по иерархии вниз (это, напомню, от базового к наследуемому), но, в отличие от `static_cast`'а, проверяет, что преобразование корректно. Если некорректно, возвращает `nullptr` в случае | ||||||
| указателей, кидает [исключение](./08_exceptions.md) `std::bad_cast` в случае ссылок. Работает это при помощи RTTI: | ||||||
|
|
||||||
|
|
@@ -492,7 +492,7 @@ struct error2 : derived { | |||||
|
|
||||||
| ### `protected`. | ||||||
| Представим, что мы пишем виджет на основе QT. Там есть базовый виджет, у которого есть операции, что делать в случае нажатия мышки, в случае перемещения колёсика и прочее подобное. Вам всё это нужно переопределить. В таком случае в базовом виджете используется ключевое слово `protected`. Оно для похожих случаев и было создано, лол. Это модификатор доступа, дающий доступ только дочерним классам и себе. | ||||||
|
|
||||||
| Стоит отметить, что `protected` предоставляет доступ только внутри дочернего класса, то есть если есть функция в `derived` классе и мы передаем в нее объект типа `base`, то обратиться к `protected` полям и функциям `base` мы все равно не сможем. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. я понимаю, что ты хочешь донести, но сформулировано непонятно, что значит "внутри"? вопрос актуален даже если забить на то, что у |
||||||
| С ним, правда, есть вопрос. Если метод не ломает инвариант, почему он не `public`, а если ломает, то хотим ли мы давать доступ дочерним классам. Тем не менее, эти вопросы не риторические, если вы нашли на них ответ — делайте `protected`. | ||||||
|
Comment on lines
+495
to
496
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. стоит разделить эти абзацы |
||||||
|
|
||||||
| ## Ещё best practices. | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -314,6 +314,38 @@ private: | |||||
| ``` | ||||||
| Теперь из деструктора мы ничего не бросаем. Но теперь мы **обязаны явно извне вызывать `flush`, если заинтересованы в получении ошибок при закрытии**. | ||||||
|
|
||||||
| Рассмотрим ситуацию, когда мы хотим в конструкторе делать что-то, что может бросить исключение, тогда, если мы выделяем какие-либо ресурсы, нам необходимо оборачивать все вызываемые функции в try-catch блок, что может быть громоздко из-за большого количества выделяемых ресурсов (так как конструктор не завершил свое исполнение, в случае исключения деструктор вызван не будет). Для данных целей может быть использован **делегирующий конструктор**. | ||||||
| ```c++ | ||||||
| struct file_descriptor { | ||||||
| private: | ||||||
| File(FILE * file) : fd(file){} | ||||||
| void init_work_with_file() {} // can throw an exception | ||||||
| public: | ||||||
| file_descriptor(const char* filename, const char* mode) | ||||||
| : File(fopen(filename, mode)) | ||||||
| { | ||||||
| init_work_with_file(); | ||||||
| } | ||||||
|
|
||||||
| void flush() { | ||||||
| int result = close(fd); | ||||||
| if (result != 0) | ||||||
| throw std::runtime_exception("File closing failed."); | ||||||
| fd = -1; | ||||||
| } | ||||||
|
|
||||||
| ~file_descriptor() { | ||||||
| if (fd != -1) | ||||||
| close(fd); | ||||||
| } | ||||||
|
|
||||||
| private: | ||||||
| FILE * fd; | ||||||
| }; | ||||||
| ``` | ||||||
|
|
||||||
| В данном примере, если в публичном конструкторе будет выброшено исключение, то утечек данных не произойдет, так как приватный конструктор уже закончит свое исполнение к моменту выброса исключения и будет вызван деструктор, который высвободит выделенные ресурсы. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. пропущена запятая:
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. а где-то написано о том, почему достаточно срабатывания приватного конструктора? если нет, лучше явно написать |
||||||
|
|
||||||
| ## Гарантии исключений. | ||||||
| Уважаемые знатоки, внимание, вопрос. Через одну минуту найдите ошибку в следующем операторе присваивания для строк: | ||||||
| ```c++ | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
вообще-то
reinterpret_castиз указателя в указатель именно так и определён в стандарте, не надо обращаться ни к каким поставщикам компиляторов (implementation-defined другие версииreinterpret_cast'а, которые здесь не упомянуты, например из указателя в число)