Великая благодарность за ссылки: "предыдущая лекция" и "следующая лекция"!! Также очень удачно подобраны размеры окон на видео. Очень удобно и на мониторе и на телефоне смотреть. Материал кайф!
Кстати меня за эти ссылки критиковали. Типа (1) есть же плейлисты и (2) можно же делать всплывающую подсказку в конце. Но я сам отсмотрел тонну образовательных видео и мне всегда чего-то такого не хватало.
std::regex у нас начиная с c++11. Я в последний раз пробовал его году в 17-м. По скорости был, мягко говоря, не очень. Тогда для себя решил, что Re2 -- гораздо лучше. Вообще на сегодняшний день вполне можно было бы сделать regex, строящий автомат в CT. Вроде бы уже всё для этого есть. И есть языки, в которых так и сделано. Это очень удобно. В debug сборке regex строится в рантайме, в release в компайл тайме.
Так хм... flex это и делает: строит всё во время компиляции. Делать такое внутри языка через constexpr или шаблоны можно (я видел доклад на cppcon по ct regex), но это будет занимать годы времени компилятора. Я как то созерцал проект, куда от самонадеянности затащили boost::spirit и это было очень долго, очень плохо и с очень плохой диагностикой.
1:00:00 "Задана операция конкатенации, которая делает из строк группу" Ну у строк не определено понятие "обратной строки" которая делает из строки пустую, поэтому скорее не группа а полугруппа
9:29 в C# с недавнего времени разрешено объявление методов с их телом, считается поведением по умолчанию, т.е. если не будет реализован метод в наследуемом классе/ структуре то поведение будет то, которое в написано интерфейсе. Можно сказать, что это как раз наследование реализации. Скоро можно будет себе отстреливать не только ноги
Нашёл на своём гитхабе лексер и парсер от cool, это было 5 лет назад. Но курс так и не окончил. Тогда курс iLab вёл парень, который нас агитировал идти в Tarantool. Забавно, но на гитхаб распознаёт cool как отдельный язык и есть даже какая-то подсветка синтаксиса для него. Кто знает, может и для ParaCL добавят :)
Да пять-шесть лет назад меня на айлабе еще не было я ещё был в софт машинс вероятно. Я думаю кул они брали 1в1 из Стэнфордовского курса. Парасил немного более локальная штука.
39:12 "мы выводим где лежим мы сами и где лежит наше d", немного оговорились, не где лежит наше d, а натурально само d. Не понятно почему адрес наследника не совпал с адресом одного из предков, из-за указателя на собственную таблицу виртуальных функций? Но разве при обычном, одиночном наследовании не так же? Тоже ведь есть таблица виртуальных функций и получается адрес объекта-наследника не будет совпадать с адресом зашитого внутри базового класса, но указатель на наследника будет приводиться к указателю на базовый класс и без static_cast. Upd: а, понял, адрес совпадет с Filler'ом.
Оффтоп(?) 54:35 Так ли это, что всегда можно избежать? Есть же исключительные ситуации, которые невозможно обработать. Например невозможность захватить мьютекс, или банально память кончилась. Тогда на main catch(...) вешать? Или к примеру использование внутри "noexcept части программы" тех же аллокаций, мьютексов и т.д., с невозможностью преодоления terminate (или это ошибочный дизайн?). Я даже с интернетом не смог разобраться. Может быть вы знаете какие-то доклады на тему аварийного завершения программы и освобождения ресурсов?
Вы не должны внутри noexcept части программы использовать что-то что может бросать исключения. Вы не должны где бы то ни было вызывать abort или std::terminate. В рамках базового курса любое другое проектирование плохое. Учить чему-то другому это преступление. Если вы пишете библиотеку и у вас кончилась память вы должны поймать bad_alloc и вернуть вызывающему коду что у вас кончилась память, отпустив все ресурсы и всё остальное. И да, я знаю, что есть программы которые спроектированы иначе (например LLVM считает возможным делать аборт по фатальной ошибке и это было моей личной головной болью три года в Интеловском графическом драйвере, который пытается использовать LLVM как библиотеку и этим нарушает L0 спек по каждому аборту). Это ничего не меняет. Проектировать так чтобы можно было вызвать аборт это всегда, несомненно и совершенно точно ошибка. Вы должны освободить все ваши ресурсы и вернуть управление, точка. Если для этого надо вставить catch в main, сделайте это. Это не обсуждается.
На диаграмме классов на 32 минуте перепутаны направления стрелочек наследования и композиции. Ромбовидный наконечник идёт на стороне объекта-агрегата, треугольный наконечник идет со стороны родительского класса
Прошу прощения за придирку относительно 47:20, понимаю, что суть объяснения касается typeid, но всё же динамический тип есть у каждого объекта, и в случае с указателем он совпадает со статическим. Простой пример несовпадения статического и динамического типов без полиморфного класса, когда мы инспектируем объект как массив char-ов. На мой взгляд корректнее говорить про полиморфный класс, как описано в стандарте [expr.typeid]#3,#4 P.S. Посмотрел лекции предыдущего и следующего годов, и там не было так сказано. Возможно это неудачная формулировка?
Да, я тут всегда балансирую между корректностью и понятностью. Конечно методически правильно считать что динамический тип есть только если есть vtbl. Но формально да, есть у всех.
Здравствуйте. Иногда на в примерах замечаю, что в main идёт выделение памяти, но нет освобождения её. Это корректное написания кода? Во всех ли случаях ОС освободит память после завершения программы?
Это некорректно. Память за собой надо всегда освобождать. Но я часто экономлю место на слайдах если это не критично. Ну и да, ОС конечно всё освободит, но это не отменяет того, что вы тоже должны это сделать.
Вставлю 5 копеек. ОС то освободит память, однако иногда пишутся такие программы, которые должны работать постоянно без перебоя. В таких случаях программа завершают редко, и ровно так же редко освобождаются утечки, а в промежутках всё больше и больше накапливается утёкшая память. Если программа год без выключения простояла (та же ОС) - скорее всего работа серьёзно замедлилась, правда значит и утекает по чучуть; а если много - программа может сама завершиться из-за исчерпания доступных ресурсов, и даже не обязательно, что пройдёт хотя-бы день. По этому нет нам лафы), если только не перейти на язык со сборщиком мусора. Освобождение памяти, не смотря на страховку ОС, не просто эстетика, перфекционизм, догматизм, но в первую очередь стабильность работы, т.к. страхует ОС только после отработки программы, а не во время (если мы говорим про память), и чем дольше должна работать программа - тем важнее эта стабильность. Понимаю, что за 2 года могли уже сами узнать это. Пишу в целом для интересующихся.
Так а почему бы this не работать в случае из начала видео? Ожидается указатель на тип, который является моей базой, так привожу свой this к указателю на мою базу и всё, либо более явно кастую, по моему никаких проблем там не должно произойти. Даже если там будут работать с объектом по указателю, то он уже сконструирован
Я не нашёл контраргументов. Если бы я их нашёл, я бы их упомянул. Мне просто не нравится эта идея. В принципе если там не делать дополнительную косвенность, а оставить this, ничего особо не поменяется, множественное наследование изобретено, идём дальше. Можете рассматривать избавление от this здесь как необязательное эстетическое усложнение.
42:30 Я написал 4 класса, 2 и 3 наследуют виртуально 1й, 4й наследует второй и третий; Создал объект 4го и вывел адрес с полем. Потом сделал статик каст на 2й и так же вывел адрес с полем. Потом сделал обратный каст из второго на 4й и вывел поле. Каждый раз адрес оставался одним и тем же! Хотя поле выводилось верно. Результат: 0x7fb1afc057a0 1 0x7fb1afc057a0 5 0x7fb1afc057a0 1 Почему так? Ведь статик каст в лекции выдает ошибку при наличии виртуального наследования, а у меня нет. И адрес на выводе не меняется.
static_cast для ошибки должен идти через виртуальную базу. Если он не проходит через смерженные таблицы, то с ним всё ок. Например вот пример со структурой похожей на вашу: я создаю объект типа D через виртуальную базу и он ломается: godbolt.org/z/KT7zWTz6K Но замените там в 20-й строчке A* на D* и он пройдёт без ошибок т.к. будет идти только через статически известную компилятору часть.
@@tilir Тонко! У меня получилось добиться ошибки в своем коде, спасибо. В практическом смысле это означает что мы, например, не сможем передать объект по базе в функцию и там специализировать его. Например так: void foo(A & pa){ B *pb = static_cast(pa); std::cout
Здравствуйте. У вас на диаграмме классов UML, для демонстрации виртуального наследования, IMHO неправильно проставлены значки наследования: исходя из вашей диаграммы класс Х это базовый класс для D1 & D2, а никак не наоборот.
Да я пока спеку не выложил. Но вообще сесть и написать свой компилятор любого C подобного языка это очень полезно и моя проверка вам там не особо нужна. Просто сделайте так чтобы он работал.
Стало любопытно: а почему виртуальные функции и виртуальное наследование вообще называются виртуальными? У понятия виртуальности как-то много определений, некоторые из них подходят. Склоняюсь к тому, что «виртуальный» в C++ означает «как настоящий» или «имитированный», то есть выглядящий полностью реальным и обычным, но на самом деле являющийся чем-то совершенно другим. - Вызов виртуальной функции выглядит вполне обычным вызовом метода для базы, но на деле неизвестно, какая функция скрывается за этой маской. - Работа с виртуальной базой кажется нормальной, будто базовая часть структуры находится на своём законном месте, а не где-то за указателем. Интересует, потому что хочется связать идею названия с идеями реализации и практического применения.
Читайте "виртуальный" как "требующий специальной поддержки в конструкторе". В случае виртуальной функции конструктор заполняет её вхождение в VMT, в случае виртуального подобъекта, записывает (на самом деле -- тоже в VMT) оффсет разделяемого региона. И то и другое он делает незаметно для программиста.
1:10:00 даже заинтересовало, как будет выглядеть ДКА, решил построить, вроде получилось, но не такое уж оно и большое вышло. Даже проверил в работе, сравнил с regex - работает. Вот пару строк на случайном генераторе. bbabababbaabcbbbcbbbcbbccccbbc babaaababaaaaababbccccccccbbcc bbabaabbbababbabbccbbbbbccbccc bbabbbaaabcccbbbccccbcbcbccccc baaabbbabbabbabaabbbbcccbbccbb bbabbbbbaababcbcbcbcbbcbbbcbbc ababaabbbaaabbababccbbcbbbcccc
Великая благодарность за ссылки: "предыдущая лекция" и "следующая лекция"!! Также очень удачно подобраны размеры окон на видео. Очень удобно и на мониторе и на телефоне смотреть. Материал кайф!
Кстати меня за эти ссылки критиковали. Типа (1) есть же плейлисты и (2) можно же делать всплывающую подсказку в конце.
Но я сам отсмотрел тонну образовательных видео и мне всегда чего-то такого не хватало.
Огромная благодарность!
std::regex у нас начиная с c++11. Я в последний раз пробовал его году в 17-м. По скорости был, мягко говоря, не очень. Тогда для себя решил, что Re2 -- гораздо лучше.
Вообще на сегодняшний день вполне можно было бы сделать regex, строящий автомат в CT. Вроде бы уже всё для этого есть.
И есть языки, в которых так и сделано. Это очень удобно. В debug сборке regex строится в рантайме, в release в компайл тайме.
Так хм... flex это и делает: строит всё во время компиляции. Делать такое внутри языка через constexpr или шаблоны можно (я видел доклад на cppcon по ct regex), но это будет занимать годы времени компилятора. Я как то созерцал проект, куда от самонадеянности затащили boost::spirit и это было очень долго, очень плохо и с очень плохой диагностикой.
1:00:00 "Задана операция конкатенации, которая делает из строк группу"
Ну у строк не определено понятие "обратной строки" которая делает из строки пустую, поэтому скорее не группа а полугруппа
Да, спасибо. Скорее даже не полугруппа, а моноид. Внесу в errata.
9:29 в C# с недавнего времени разрешено объявление методов с их телом, считается поведением по умолчанию, т.е. если не будет реализован метод в наследуемом классе/ структуре то поведение будет то, которое в написано интерфейсе. Можно сказать, что это как раз наследование реализации. Скоро можно будет себе отстреливать не только ноги
Это похоже на семантику extern inline из C99.
P. S. этот канал явно не предназначен для обсуждения C#
Нашёл на своём гитхабе лексер и парсер от cool, это было 5 лет назад. Но курс так и не окончил. Тогда курс iLab вёл парень, который нас агитировал идти в Tarantool. Забавно, но на гитхаб распознаёт cool как отдельный язык и есть даже какая-то подсветка синтаксиса для него. Кто знает, может и для ParaCL добавят :)
Да пять-шесть лет назад меня на айлабе еще не было я ещё был в софт машинс вероятно. Я думаю кул они брали 1в1 из Стэнфордовского курса. Парасил немного более локальная штука.
39:12 "мы выводим где лежим мы сами и где лежит наше d", немного оговорились, не где лежит наше d, а натурально само d.
Не понятно почему адрес наследника не совпал с адресом одного из предков, из-за указателя на собственную таблицу виртуальных функций? Но разве при обычном, одиночном наследовании не так же? Тоже ведь есть таблица виртуальных функций и получается адрес объекта-наследника не будет совпадать с адресом зашитого внутри базового класса, но указатель на наследника будет приводиться к указателю на базовый класс и без static_cast.
Upd: а, понял, адрес совпадет с Filler'ом.
Оффтоп(?) 54:35 Так ли это, что всегда можно избежать? Есть же исключительные ситуации, которые невозможно обработать. Например невозможность захватить мьютекс, или банально память кончилась. Тогда на main catch(...) вешать? Или к примеру использование внутри "noexcept части программы" тех же аллокаций, мьютексов и т.д., с невозможностью преодоления terminate (или это ошибочный дизайн?). Я даже с интернетом не смог разобраться.
Может быть вы знаете какие-то доклады на тему аварийного завершения программы и освобождения ресурсов?
Вы не должны внутри noexcept части программы использовать что-то что может бросать исключения.
Вы не должны где бы то ни было вызывать abort или std::terminate.
В рамках базового курса любое другое проектирование плохое. Учить чему-то другому это преступление. Если вы пишете библиотеку и у вас кончилась память вы должны поймать bad_alloc и вернуть вызывающему коду что у вас кончилась память, отпустив все ресурсы и всё остальное.
И да, я знаю, что есть программы которые спроектированы иначе (например LLVM считает возможным делать аборт по фатальной ошибке и это было моей личной головной болью три года в Интеловском графическом драйвере, который пытается использовать LLVM как библиотеку и этим нарушает L0 спек по каждому аборту). Это ничего не меняет. Проектировать так чтобы можно было вызвать аборт это всегда, несомненно и совершенно точно ошибка. Вы должны освободить все ваши ресурсы и вернуть управление, точка. Если для этого надо вставить catch в main, сделайте это.
Это не обсуждается.
мелькая опечатка на слайде 78, самая нижняя строка
IOFile f; // вызовет File(smth3)
заменить на
IOFile f; // вызовет File(smths3)
Добрый вечер!
1:00:35. Это не группа. Нет обратных элементов.
Спасибо, но вы второй =) Это уже есть в errata. Вот за что я люблю свою аудиторию, группу с моноидом тут перепутать никто не даст ))
Большое спасибо! Очень интересно послушать лекцию по автоматам, но в описании не нашел
На этом же канале: ua-cam.com/video/M2XqgaJXjhM/v-deo.html
На диаграмме классов на 32 минуте перепутаны направления стрелочек наследования и композиции. Ромбовидный наконечник идёт на стороне объекта-агрегата, треугольный наконечник идет со стороны родительского класса
Первый раз это замечание появилось в комментариях к этому видео год назад, но всё равно спасибо за наблюдательность ))
@@tilir , все равно, спасибо за ваш курс, очень систематически рассказываете.
Прошу прощения за придирку относительно 47:20, понимаю, что суть объяснения касается typeid, но всё же динамический тип есть у каждого объекта, и в случае с указателем он совпадает со статическим. Простой пример несовпадения статического и динамического типов без полиморфного класса, когда мы инспектируем объект как массив char-ов. На мой взгляд корректнее говорить про полиморфный класс, как описано в стандарте [expr.typeid]#3,#4
P.S. Посмотрел лекции предыдущего и следующего годов, и там не было так сказано. Возможно это неудачная формулировка?
Да, я тут всегда балансирую между корректностью и понятностью. Конечно методически правильно считать что динамический тип есть только если есть vtbl. Но формально да, есть у всех.
Здравствуйте. Иногда на в примерах замечаю, что в main идёт выделение памяти, но нет освобождения её. Это корректное написания кода? Во всех ли случаях ОС освободит память после завершения программы?
Это некорректно. Память за собой надо всегда освобождать. Но я часто экономлю место на слайдах если это не критично. Ну и да, ОС конечно всё освободит, но это не отменяет того, что вы тоже должны это сделать.
Вставлю 5 копеек.
ОС то освободит память, однако иногда пишутся такие программы, которые должны работать постоянно без перебоя. В таких случаях программа завершают редко, и ровно так же редко освобождаются утечки, а в промежутках всё больше и больше накапливается утёкшая память. Если программа год без выключения простояла (та же ОС) - скорее всего работа серьёзно замедлилась, правда значит и утекает по чучуть; а если много - программа может сама завершиться из-за исчерпания доступных ресурсов, и даже не обязательно, что пройдёт хотя-бы день. По этому нет нам лафы), если только не перейти на язык со сборщиком мусора. Освобождение памяти, не смотря на страховку ОС, не просто эстетика, перфекционизм, догматизм, но в первую очередь стабильность работы, т.к. страхует ОС только после отработки программы, а не во время (если мы говорим про память), и чем дольше должна работать программа - тем важнее эта стабильность.
Понимаю, что за 2 года могли уже сами узнать это. Пишу в целом для интересующихся.
Так а почему бы this не работать в случае из начала видео? Ожидается указатель на тип, который является моей базой, так привожу свой this к указателю на мою базу и всё, либо более явно кастую, по моему никаких проблем там не должно произойти.
Даже если там будут работать с объектом по указателю, то он уже сконструирован
Я не нашёл контраргументов. Если бы я их нашёл, я бы их упомянул. Мне просто не нравится эта идея. В принципе если там не делать дополнительную косвенность, а оставить this, ничего особо не поменяется, множественное наследование изобретено, идём дальше. Можете рассматривать избавление от this здесь как необязательное эстетическое усложнение.
42:30 Я написал 4 класса, 2 и 3 наследуют виртуально 1й, 4й наследует второй и третий; Создал объект 4го и вывел адрес с полем. Потом сделал статик каст на 2й и так же вывел адрес с полем. Потом сделал обратный каст из второго на 4й и вывел поле.
Каждый раз адрес оставался одним и тем же! Хотя поле выводилось верно. Результат:
0x7fb1afc057a0 1
0x7fb1afc057a0 5
0x7fb1afc057a0 1
Почему так? Ведь статик каст в лекции выдает ошибку при наличии виртуального наследования, а у меня нет. И адрес на выводе не меняется.
static_cast для ошибки должен идти через виртуальную базу. Если он не проходит через смерженные таблицы, то с ним всё ок.
Например вот пример со структурой похожей на вашу: я создаю объект типа D через виртуальную базу и он ломается: godbolt.org/z/KT7zWTz6K
Но замените там в 20-й строчке A* на D* и он пройдёт без ошибок т.к. будет идти только через статически известную компилятору часть.
@@tilir Тонко! У меня получилось добиться ошибки в своем коде, спасибо.
В практическом смысле это означает что мы, например, не сможем передать объект по базе в функцию и там специализировать его. Например так:
void foo(A & pa){
B *pb = static_cast(pa);
std::cout
Здравствуйте. У вас на диаграмме классов UML, для демонстрации виртуального наследования, IMHO неправильно проставлены значки наследования: исходя из вашей диаграммы класс Х это базовый класс для D1 & D2, а никак не наоборот.
Да всегда с этим путался. Надо будет перерисовать.
Запомнить можно легко используя правило: широкий конец стрелки в сторону расширенного класса, то есть класса-наследника :)
Отлично )) Обожаю дурацкие мнемонические правила. Добавлю это в свою лекцию по solid (будет позже в этом курсе).
Я тоже кое что запомнил из ваших лекций: "Акисление на аноде". Такое не забыть:)
Тоже бросилось в глаза. И даже тупанул на задачке не мог понять что не так потом сообразил - стрелки
Великолепная лекция! Задание доступно только студентам? Было бы интересно изучить.
Вы о каком именно задании?
@@tilir парасил, если я правильно расслышал
Да я пока спеку не выложил. Но вообще сесть и написать свой компилятор любого C подобного языка это очень полезно и моя проверка вам там не особо нужна. Просто сделайте так чтобы он работал.
Стало любопытно: а почему виртуальные функции и виртуальное наследование вообще называются виртуальными? У понятия виртуальности как-то много определений, некоторые из них подходят.
Склоняюсь к тому, что «виртуальный» в C++ означает «как настоящий» или «имитированный», то есть выглядящий полностью реальным и обычным, но на самом деле являющийся чем-то совершенно другим.
- Вызов виртуальной функции выглядит вполне обычным вызовом метода для базы, но на деле неизвестно, какая функция скрывается за этой маской.
- Работа с виртуальной базой кажется нормальной, будто базовая часть структуры находится на своём законном месте, а не где-то за указателем.
Интересует, потому что хочется связать идею названия с идеями реализации и практического применения.
Читайте "виртуальный" как "требующий специальной поддержки в конструкторе". В случае виртуальной функции конструктор заполняет её вхождение в VMT, в случае виртуального подобъекта, записывает (на самом деле -- тоже в VMT) оффсет разделяемого региона. И то и другое он делает незаметно для программиста.
1:10:00 даже заинтересовало, как будет выглядеть ДКА, решил построить, вроде получилось, но не такое уж оно и большое вышло. Даже проверил в работе, сравнил с regex - работает. Вот пару строк на случайном генераторе.
bbabababbaabcbbbcbbbcbbccccbbc
babaaababaaaaababbccccccccbbcc
bbabaabbbababbabbccbbbbbccbccc
bbabbbaaabcccbbbccccbcbcbccccc
baaabbbabbabbabaabbbbcccbbccbb
bbabbbbbaababcbcbcbcbbcbbbcbbc
ababaabbbaaabbababccbbcbbbcccc
Теперь можете потренироваться на выражении ((ab.*cd) | (ef.*gh)). Например abefcd подходит, а efefcd нет. Там получается очень красивый DFA.