На 42 - 43 минуте сначала вызывается деструктор локальной копии Base, а потом уже деструктор оригинального Derived и его базы. А на лекции это звучит будто при наследовании первым вызывается деструктор базового класса.
Тоже обратил на это внимание. До такой степени засомневался в своих знаниях порядка вызова деструкторов, что полез проверять. Только потом понял, что на видео сначала вызван деструктор срезанного до Base объекта, а потом объекта, сконструированного от Derived. Но изложение "сначала будет вызван деструктор Base, а потом Derived" несомненно путает.
Посмотрев следующую лекцию, я пришёл к выводу, что это очень грустная история. Ведь если залетят сразу два исключения, то придётся делать аборт, а аборт - это убийство :(
42:40 Вы говорите: "т. е. на экране будет cначала деструктор ~Base, потом деструктор ~Derived, а потом деструктор Base для этого объекта, потому что это новый объект". Но деструкторы вызываются в порядке от дочерних к базовому. На самом деле объекты будут уничтожаться после обработки исключения в обратном порядке: сначала новый - Base, за ним старый - Derived. Base1() Derived1() Base2(const&) catch ~Base2() ~Derived1() ~Base1()
Это же логично. Срезанная копия уничтожается раньше, так как создалась позже из объекта, который где-то лежит, ведь его могут бросить заново и его нельзя уничтожить до выхода из обработки
28:34 при переходе с 20 на 21 слайд, возможно я ошибаюсь, но фрейм с деструктором 0x...c70 уже же сняли, не должен ли обновиться адрес в this у деструктора на строчке #0 бэктрейса? Это ведь уже деструктор другого объекта. На мой взгляд пример на слайде 23 не такой уж и абсурдный, есть у нас какая-то математическая библиотека, на базе нее построили консольное приложение, там вывели в catch блоке сообщение на консоль. Завтра реализовали калькулятор с кнопочками и выводить ошибку будем в модальное окно. Важно, что в самой функции divide мы не знаем из какого типа приложения нас вызывают и поэтому нельзя взять и просто вывести на консоль, ее может и не быть.
Я бы не сказал, что Си слишком много знает о сигналах. Там полторы функции базовой поддержки. Сигналы это в основном POSIX, систем без POSIX довольно много.
У вас есть видео по кодировкам, им их преобразования? Например, я вообще не понимаю, почему в символах не используются unsigned char, а просто char, может дело, что включённый старший бит означает, что это не один символ в один бай, а ищи другой байт и также проверяй?
Нет, таких видео у меня нет. Я пытался читать такие лекции, но на них все засыпали -- там масса довольно неинтересных деталей. Может быть рано или поздно я придумаю как это подать. К счастью в интернете много таких видео и на zerocost conf этого года было даже на русском.
-На --1:26:00-- в цикле while (tmp.size() < used_) tmp.push(arr_[tmp.size()]), разве не должно быть tmp.push(arr_[tmp.used()], если size является аналогом capacity из стандартного вектора?- *UPD.* Увидел в следующей лекции, что size() возвращает used_.
Если я правильно понял технику линии Калба: мы создаем временный объект, с которым делаем все необходимые нам операции - выделение памяти и т.д., в течение которых может вылететь исключение - это все выше этой линии( не меняем состояние, а можем бросить исключение), а затем, лишь меняем этот временный объект с текущим. Вопрос, так действительно желательно(настоятельно рекомендуется) писать большинство классов(по типу как std::vector)?
На 49 слайде разбирается, что 6-ой из 10 операторов присваивания Т может бросить исключение. А в строчке arr_= new T[rhs.size_] 6-ой из 10 конструкторов по умолчанию T также может бросить исключение. Про это ни слова не сказано. Я бы студентам рассказал про гарантии new и, в частности, для new[] (commit/rollback?). Со всем уважением! Но я, рожденный в стране советов, не могу удержаться от советов))
Спасибо отличный комментарий. Ничего не сказано именно из за наличия этих гарантий -- я привык что там всё хорошо и можно не думать. Надо будет это упомянуть во второй части этой лекции.
1. Механизм исключительных ситуаций, в котором их понимает C++, тесно завязан на явном понимании/представлении, что такое стек вызовов... Абстрактная машина языка С++ никак, насколько я знаю, не описывает стеки вызовов и данных, поэтому при работе с исключениями возникают проблемы. Исключения бы стали самим самой разумеющимися, если бы язык давал явное описание фрейма функции. ua-cam.com/video/COEv2kq_Ht8/v-deo.html 2. Механизм исключений по своему поведению очень похож на механизм goto, который не приветствуется в разработке. Оба этих механизма невероятные возможности изменения поведения программы, но требуют недюжего ума, чтобы уследить за всем...
1. Наоборот. То что стандарт даёт реализациям свободу в пределах очерченного абстрактной машиной в итоге позволило сделать исключения довольно эффективными. 2. Исключения сложнее чем обычный goto, но гораздо проще, чем многопоточность. То есть это сложный но не слишком сложный механизм.
Нет, там всё просто !(failbit || badbit). То есть это !fail(). В силу общей странности конструкции потоков там скорее вызов good() не означает, что всё хорошо. А вот на operator bool() можно ориентироваться, он работает интуитивно.
Здравствуйте, могу ошибаться, пожалуйста проверьте 5-й слайд: atoi, atol, atoll - возвращает 0 в случае невозможности преобразования и в случае переполнения "behavior is undefined" ист. cppreference по atoi источник freebsd org : "ERRORS The function atoi() need not affect the value of errno on an error." В том же источнике характерные errno выставляет strtonum. "The HUGE_VALF, HUGE_VAL and HUGE_VALL macros expand to positive floating point constant expressions which compare equal to the values returned by floating-point functions and operators in case of overflow " , вероятно тогда общее описание на слайде подходит для преобразования в число с плавающей точкой strtod, strtof, strtold Спасибо за лекции!
Да, надо заменить atoi на strtol, спасибо. Я смотрел в K&R B.5 где указано что atoi строго эквивалентна (int)strtol(s, (char **)NULL, 10). Но я сейчас глянул в C11 там поправили как вы сказали, сделав UB на ошибке.
На 42 - 43 минуте сначала вызывается деструктор локальной копии Base, а потом уже деструктор оригинального Derived и его базы.
А на лекции это звучит будто при наследовании первым вызывается деструктор базового класса.
Тоже обратил на это внимание. До такой степени засомневался в своих знаниях порядка вызова деструкторов, что полез проверять. Только потом понял, что на видео сначала вызван деструктор срезанного до Base объекта, а потом объекта, сконструированного от Derived. Но изложение "сначала будет вызван деструктор Base, а потом Derived" несомненно путает.
@@d7d1cd аналогично)
Летят два исключения. Одно говорит:
- Жаль, погода нелетная.
Другое:
- Ничего. Лишь бы программист хороший попался.
Спасибо за лекцию!
Посмотрев следующую лекцию, я пришёл к выводу, что это очень грустная история. Ведь если залетят сразу два исключения, то придётся делать аборт, а аборт - это убийство :(
42:40 Вы говорите: "т. е. на экране будет cначала деструктор ~Base, потом деструктор ~Derived, а потом деструктор Base для этого объекта, потому что это новый объект". Но деструкторы вызываются в порядке от дочерних к базовому. На самом деле объекты будут уничтожаться после обработки исключения в обратном порядке: сначала новый - Base, за ним старый - Derived.
Base1() Derived1() Base2(const&)
catch
~Base2() ~Derived1() ~Base1()
Из стандарта [except.throw]#4
Да, тоже это заметил и не полнеился написать код))) Было всё как вы сказали.
Это же логично. Срезанная копия уничтожается раньше, так как создалась позже из объекта, который где-то лежит, ведь его могут бросить заново и его нельзя уничтожить до выхода из обработки
Линию Калба можно например границей (линией) исключений (exceptions border (line)) назвать, запомнить легче и сразу понятно что это.
28:34 при переходе с 20 на 21 слайд, возможно я ошибаюсь, но фрейм с деструктором 0x...c70 уже же сняли, не должен ли обновиться адрес в this у деструктора на строчке #0 бэктрейса? Это ведь уже деструктор другого объекта.
На мой взгляд пример на слайде 23 не такой уж и абсурдный, есть у нас какая-то математическая библиотека, на базе нее построили консольное приложение, там вывели в catch блоке сообщение на консоль. Завтра реализовали калькулятор с кнопочками и выводить ошибку будем в модальное окно. Важно, что в самой функции divide мы не знаем из какого типа приложения нас вызывают и поэтому нельзя взять и просто вывести на консоль, ее может и не быть.
Интересный смысл для такой конструкции =)
17:00 разве при undefined instruction exception операционка не посылает какой-нибудь сигнал (который можно перехватить)?
Стандарт языка C++ ничего не знает про сигналы.
Ничего себе. Си знает, а с++ не знает. И что, есть какие-то системы, где есть с++, но нет си и сигналов?
Я бы не сказал, что Си слишком много знает о сигналах. Там полторы функции базовой поддержки. Сигналы это в основном POSIX, систем без POSIX довольно много.
У вас есть видео по кодировкам, им их преобразования? Например, я вообще не понимаю, почему в символах не используются unsigned char, а просто char, может дело, что включённый старший бит означает, что это не один символ в один бай, а ищи другой байт и также проверяй?
Нет, таких видео у меня нет. Я пытался читать такие лекции, но на них все засыпали -- там масса довольно неинтересных деталей. Может быть рано или поздно я придумаю как это подать. К счастью в интернете много таких видео и на zerocost conf этого года было даже на русском.
Так что получается, нужно все заворачивать в try-catch?
-На --1:26:00-- в цикле while (tmp.size() < used_) tmp.push(arr_[tmp.size()]), разве не должно быть tmp.push(arr_[tmp.used()], если size является аналогом capacity из стандартного вектора?-
*UPD.* Увидел в следующей лекции, что size() возвращает used_.
47:01 Ого! Интересно. Вопрос про разыменование null указателя заиграл новыми красками. Можно на собеседованиях покрасоваться.
Ещё внутри noexcept-expression можно.
godbolt.org/z/6dz3r6P36
1:11:43 Можно уточнить, почему всё-таки будет исключение в copy ctor в цикле? Или это просто предположение а вдруг?
Это предположение. Когда вы пишете обобщенный код, его разумно сделать.
Что по стандарту должно произойти при двойном освобождении памяти?
UB
Если я правильно понял технику линии Калба: мы создаем временный объект, с которым делаем все необходимые нам операции - выделение памяти и т.д., в течение которых может вылететь исключение - это все выше этой линии( не меняем состояние, а можем бросить исключение), а затем, лишь меняем этот временный объект с текущим. Вопрос, так действительно желательно(настоятельно рекомендуется) писать большинство классов(по типу как std::vector)?
Гайдлайн что лучше и как нужно тут дать сложно, случаи разные бывают. Но я бы советовал так проектировать если вам нужна строгая гарантия.
На 51:41 некорректная формулировка?. По [except.throw]#6#7 внутри catch блока исключение уже не активно.
Да, нечто вроде того. Но это базовый курс, не надо требовать от него магистерской точности. Идею я донёс, я полагаю.
На 49 слайде разбирается, что 6-ой из 10 операторов присваивания Т может бросить исключение.
А в строчке arr_= new T[rhs.size_] 6-ой из 10 конструкторов по умолчанию T также может бросить исключение.
Про это ни слова не сказано.
Я бы студентам рассказал про гарантии new и, в частности, для new[] (commit/rollback?).
Со всем уважением! Но я, рожденный в стране советов, не могу удержаться от советов))
Спасибо отличный комментарий. Ничего не сказано именно из за наличия этих гарантий -- я привык что там всё хорошо и можно не думать. Надо будет это упомянуть во второй части этой лекции.
1:20:04 в цикле перед приращенем запятая вместо ;
Спасибо, добавил в errata.
1. Механизм исключительных ситуаций, в котором их понимает C++, тесно завязан на явном понимании/представлении, что такое стек вызовов...
Абстрактная машина языка С++ никак, насколько я знаю, не описывает стеки вызовов и данных, поэтому при работе с исключениями возникают проблемы. Исключения бы стали самим самой разумеющимися, если бы язык давал явное описание фрейма функции.
ua-cam.com/video/COEv2kq_Ht8/v-deo.html
2. Механизм исключений по своему поведению очень похож на механизм goto, который не приветствуется в разработке. Оба этих механизма невероятные возможности изменения поведения программы, но требуют недюжего ума, чтобы уследить за всем...
1. Наоборот. То что стандарт даёт реализациям свободу в пределах очерченного абстрактной машиной в итоге позволило сделать исключения довольно эффективными.
2. Исключения сложнее чем обычный goto, но гораздо проще, чем многопоточность. То есть это сложный но не слишком сложный механизм.
Вроде бы оператор бул у стрима не равносилен good / bad, там всё несколько запутанней...
Нет, там всё просто !(failbit || badbit). То есть это !fail(). В силу общей странности конструкции потоков там скорее вызов good() не означает, что всё хорошо. А вот на operator bool() можно ориентироваться, он работает интуитивно.
Вроде назван у курса 'базовый' но я все равно местамт не понимаю о чем идет речь, требуется знание предыдущих серий видимо. . )
А что именно не ясно?
Например 15:20 setjump and longjump
Если я правильно написал. Такие слова в первый раз услышал
Да это из курса по языку C. Но я думаю это не так сложно нагуглить...
1910
Здравствуйте, могу ошибаться, пожалуйста проверьте 5-й слайд:
atoi, atol, atoll - возвращает 0 в случае невозможности преобразования и в случае переполнения "behavior is undefined" ист. cppreference
по atoi источник freebsd org : "ERRORS The function atoi() need not affect the value of errno on an error." В том же источнике характерные errno выставляет strtonum.
"The HUGE_VALF, HUGE_VAL and HUGE_VALL macros expand to positive floating point constant expressions which compare equal to the values returned by floating-point functions and operators in case of overflow " , вероятно тогда общее описание на слайде подходит для преобразования в число с плавающей точкой strtod, strtof, strtold
Спасибо за лекции!
Да, надо заменить atoi на strtol, спасибо. Я смотрел в K&R B.5 где указано что atoi строго эквивалентна (int)strtol(s, (char **)NULL, 10). Но я сейчас глянул в C11 там поправили как вы сказали, сделав UB на ошибке.