Базовый курс C++ (MIPT, ILab). Lecture 8. Наследование и полиморфизм

แชร์
ฝัง
  • เผยแพร่เมื่อ 18 ก.ย. 2024

ความคิดเห็น • 69

  • @std-sort
    @std-sort 2 หลายเดือนก่อน +4

    Не встречал на русском более качественного курса чем у Вас. Большое спасибо за труд :-)

  • @kotanvich
    @kotanvich ปีที่แล้ว +11

    Какие же крутые лекции! Но как же хотелось бы увидеть лекции по си в вашем исполнении!

  • @samolisov
    @samolisov 2 ปีที่แล้ว +9

    1:34:41 Паттерн "Public Морозов"

  • @evgenyrozhnowsky6572
    @evgenyrozhnowsky6572 ปีที่แล้ว +1

    Спасибо за лекции! Безумно интересно)
    Есть опечатка в слайде №54 (1:35:08 ) строки struct Derived : public Base, необходимо : public BaseNVI

  • @ufabiz
    @ufabiz 2 ปีที่แล้ว +1

    Спасибо за очередную замечательную лекцию!
    По поводу замечания в 55:41 писать или не писать virtual. Это от части code-style. Но у привычки писать virtual есть одно важное преимущество:
    Когда читаешь код, virtual у overrided функций помогают лучше видеть интерфейс класса.
    virtual всегда идёт перед функцией, его проще увидеть.
    Разумеется, это замечание не относится к ревью, а только к правильно написанному коду.

    • @tilir
      @tilir  2 ปีที่แล้ว +2

      Мне кажется override это довольно длинная и заметная аннотация...

    • @bochkarevartem
      @bochkarevartem 2 ปีที่แล้ว

      @@tilir кстати, почему override решили сделать после функции? Казалось бы, лучше было просто перед функцией вместо virtual ставить override...

    • @tilir
      @tilir  2 ปีที่แล้ว +1

      @@bochkarevartem большинство аннотаций после. Мне скорее удивительно что virtual до.

    • @bochkarevartem
      @bochkarevartem 2 ปีที่แล้ว

      @@tilir раз virtual был до, то и override логично было бы сделать до. Единственное, что override как расширение разных компиляторов существовал до включения в стандарт и как раз везде шел после. Ну и кстати, аттрибуты появились в стандарте одновременно с override, и идут перед. Вполне могло быть [[override]], но, видимо, решили сделать как уже устоялось. Просто вдруг были какие-то особые причины, о которых я даже не задумывался..

    • @tilir
      @tilir  2 ปีที่แล้ว

      @@bochkarevartem мне таких причин неизвестно. Но с другой стороны мне и глаза особо не режет, что он после.
      Я не думаю что это мог бы быть атрибут. Всё-таки override это что-то более фундаментально. Атрибуты обычно касаются только самой функции. Override контролирует свойство семейства классов в которых входит динамически полиморфная функция.
      Мне вот ещё непонятно почему нельзя было запилить это как ключевое слово...

  • @ivankorotkov2563
    @ivankorotkov2563 2 ปีที่แล้ว +9

    Спасибо!
    Вопрос по поводу слайда 24 (48:25).
    Насколько мне известно работа с vptr все-таки обычно организована несколько иначе. На слайде приведена упрощенная картина для более простого понимания, или есть какие-то специфические имплементации, устроенные таким образом?
    Насколько мне известно в момент компиляции программы уже известны какие виртуальные функции должна содержать таблица виртуальных функций для каждого класса, поэтому перед выполнением программы все таблицы виртуальных функций уже содержатся в статических данных, а все что делают конструкторы-деструкторы, это просто переставляют указатель vptr конкретных экземпляров классов на эти таблицы. И судя по листингу ассемблера эта таблица не обязана заканчиваться nullptr.
    Я проверил по нескольким компиляторам и там нигде нет динамической аллокации, а работает именно как я описал.
    Если предположить что конструктор должен динамически выделять память под vtable, то в голову приходит как минимум пара проблем:
    1) должны ли разные экземпляры одного класса разделять одну таблицу, или у каждого экземпляра должен быть свой экземпляр? Если каждый инстанс хранит свою копию таблицы, то это выглядит супер дорого, если нет, то кто отвечает за удаление таблицы.
    2) поскольку производные классы могут накидывать дополнительные поля в таблицу виртуальных функций, то базовый класс на момент создания не может знать размер таблицы виртуальных функций. И либо производные классы должны ее переаллоцировать, либо в базовый конструктор нужно еще прокидывать неявный параметр на реальный размер таблицы для этого класса, ну или еще какое-нибудь костыльное решение. Все это выглядит не очень, на фоне просто метода, описанного выше.

    • @tilir
      @tilir  2 ปีที่แล้ว +3

      Разумеется это очень упрощенная картинка. Спасибо за развернутый комментарий )) Книжка Липмана по деталям объектной модели будет у меня в списке литературы.

  • @samolisov
    @samolisov 2 ปีที่แล้ว +2

    Кстати, вспомнил, как можно вызвать деструктор наследника, если есть ссылка на базовый класс и у того деструктор невиртуальный. Есть же правило, что константные ссылки продляют время жизни временных объектов и такой код const Basic &b = make_derived() вполне валиден если make,_derived() возвращает объект класса Derived по значению. Тогда при окончании времени жизни b будет вызван деструктор временного объекта, Derived, а не Basic, несмотря на тип ссылки. И здесь неважно виртуальный деструктор у Basic или нет.

  • @1968ussr
    @1968ussr 6 หลายเดือนก่อน

    Приятные воспоминания. Второй курс, только не компилятор, а интерпретатор, мощного и лаконичного подмножества русского языка. Жаль только на кафедре к этому отнеслись немного странно. 😉

  • @ДмитрийСадков-е8ц
    @ДмитрийСадков-е8ц 2 ปีที่แล้ว +2

    Константин, похоже на слайде 43 опечатка:
    нужно заменить
    unique_ptr(T *ptr = nullptr, Deleter del = Deleter()) :
    Deleter(del), ptr_(ptr), del_(del) {}
    на это
    unique_ptr(T *ptr = nullptr, Deleter del = Deleter()) :
    Deleter(del), ptr_(ptr) {}

    • @tilir
      @tilir  2 ปีที่แล้ว

      Да вы правы. Самое смешное, что мне понадобилось вглядываться в то что вы написали чтобы осознать отличие =) Спасибо, забыл убрать при эволюциях кода.

    • @ДмитрийСадков-е8ц
      @ДмитрийСадков-е8ц 2 ปีที่แล้ว +1

      @@tilir таже самая опечатка есть на слайде 64.

    • @ДмитрийСадков-е8ц
      @ДмитрийСадков-е8ц 2 ปีที่แล้ว +1

      еще микро опечатка на слайде 57:
      ...эффективно воВзводить разреженные матрицы...

  • @Indev29
    @Indev29 2 ปีที่แล้ว

    Спасибо за лекции!
    1:29:25 Насколько я понимаю, early/late binding - это про связывание имени с конкретным типом, в C++ же имена всегда(?) связываются статически.
    Терминология довольно расплывчатая и сбивает с толку. Вплоть до того, что статьи Late Binding и Dynamic Dispatch на вики напрямую друг другу противоречат в разделе о C++.
    Есть точка зрения, что late binding в C++ не поддерживается и называть dynamic dispatch (которым является вызов виртуальных функций и наверное любой type erasure в языке) late binding'ом некорректно.
    При этом постоянно вижу, что late binding и dynamic dispatch используются как взаимозаменяемые понятия, а потому не могу уложить их в голове, ведь они означают совершенно разные вещи.
    Можете что-то сказать по этому поводу?

    • @tilir
      @tilir  2 ปีที่แล้ว

      У нас есть связывание на уровне языка: оно бывает статическим или динамическим (через виртуальные функции). И у нас есть слой под языком, когда статическое (на уровне языка) связывание может реализоваться через механизмы разделяемых библиотек и т.п. Это ортогональные вещи. На этих лекциях я под статическим и динамическим связыванием имею в виду только связывание на уровне языка, независимо от того какие реальные механизмы связывания работают там, под водой.

  • @Vakenrovec
    @Vakenrovec 2 ปีที่แล้ว

    1:00:40
    В строке ISquare *sq = new Triangle; delete sq;
    Откуда operator delete знает сколько байт нужно освободить? Почему он освободит sizeof(Triangle), а не sizeof(ISquare)?

  • @sehzadeselim863
    @sehzadeselim863 ปีที่แล้ว +2

    На задании 1:50:30 может, можно проинициализировать IBuffer нашим MyBuffer? 😅Или это вызовет проблемы, которые я не вижу?(

  • @Юлий-ы5г
    @Юлий-ы5г 2 ปีที่แล้ว +1

    Как же хорошо, что в С++ union без конструктора это ошибка компиляции. Ведь могли бы выбирать конструктор поля основываясь, например, на прогнозе погоды...

  • @oltimuss
    @oltimuss 2 ปีที่แล้ว

    На слайдах 13 и 14 (27:14) видимо произошла путаница для определений double_square. На 13 принимает Square &s, но не использует; а на 14 нет аргументов, но использует s в теле метода.
    P.S. Огромное спасибо за познавательные лекции!

    • @tilir
      @tilir  2 ปีที่แล้ว +1

      Спасибо за внимательность. Ошибка на 13-м слайде, там должен быть просто метод.

  • @goczt
    @goczt 2 ปีที่แล้ว +4

    51:24 Опечатка "SparceMatrix" - должно быть "SparseMatrix". Отследить что-то кроме орфографических ошибок компетенции не хватает

    • @tilir
      @tilir  2 ปีที่แล้ว

      Тоже неплохо, спасибо, поправлю на слайдах.

  • @samolisov
    @samolisov 2 ปีที่แล้ว +1

    User-provided vs user-declared, чувствуешь себя немного юристом, когда такое читаешь. "Проценты за месяц начисляются по минимальной сумме, которая была на счёте в этот месяц, если хоть в один день на счёте был ноль, то проценты не начисляются." - А если я открыл счёт второго числа, то первого на нем был ноль? - Нет, первого числа счёт не существовал, а значит на нем не был ноль. Вот какие-то такие аналогии на ум приходят, когда стандарт читаешь.

    • @tilir
      @tilir  2 ปีที่แล้ว +2

      Я обычно оставляю такое за кадром базового курса. Но тут как-то уж очень к слову пришлось...

  • @The604FX
    @The604FX ปีที่แล้ว +1

    Здравствуйте.
    Не совсем понимаю, как функции "хранятся" (понимаю, что не хранятся они там) внутри класса? То есть, по факту, это функции, которые принимают неявно this, но как это реализовано на практике?
    При использовании пространств имён механизм схожий?

    • @tilir
      @tilir  ปีที่แล้ว +2

      Функция не "хранится внутри класса". Функция-член это просто функция с манглированным именем принимающая this первым неявным аргументом. Пространство имён это плюс к манглированию имени. Я собственно показываю как это устроено в первых лекциях курса. Возможно вы слишком забежали вперёд.

  • @ДмитрийАндреев-ф3х
    @ДмитрийАндреев-ф3х 5 หลายเดือนก่อน

    Имеет ли смысл внутри дочернего класса вызвать using родительского?
    как на 1:43:23 using Matrix; вместо using Matrix::pow;

  • @razenkovv
    @razenkovv 7 หลายเดือนก่อน

    Здравствуйте, не очень понял на 1:50:00. Порядок инициализации объектов же не зависит от их порядка в списке инициализации? То есть Array будет проинициализирован первым не зависимо от того на каком месте он будет в списке инициализации. И он будет проиницилизирован указателем на непроинициализированную область памяти, и соответственно потом это останется висячим указателем. Но такой код скомпилируется. (Рассуждения для себя, если я ошибаюсь, буду рад если кто нибудь поправит)) Автору канала огромное спасибо за такие лекции!

    • @tilir
      @tilir  6 หลายเดือนก่อน

      Нет, порядок инициализации подобъектов не зависит от их порядка в списке инициализации, а только от порядка внутри определения класса.

    • @razenkovv
      @razenkovv 6 หลายเดือนก่อน

      @@tilir Ага, понял, спасибо

  • @ddvamp
    @ddvamp ปีที่แล้ว +1

    Добрый вечер. Почему бы на 9:52 просто не использовать mem-initializer-list вместо placement new?
    union U {
    std::string s_;
    std::vector v_;
    U(std::string s) : s_(std::move(s)) {}
    U(std::vector v) : v_(std::move(v)) {}
    ~U() {}
    };

    • @tilir
      @tilir  ปีที่แล้ว +2

      Не знал что так можно. Это несколько реабилитирует union как технику в C++.
      Оставлю для памяти: godbolt.org/z/f5cTe9v55

  • @qp7059
    @qp7059 ปีที่แล้ว

    Если создаем абстрактный класс, компилятор выдает варнинг: виртуальная таблица будет создана в каждой единице трансляции. Добавление какой-либо функции с телом в .cpp решает эту проблему. Есть ли другие способы решения этой проблемы?

  • @bkWorm-gx2pi
    @bkWorm-gx2pi 4 หลายเดือนก่อน

    А если и в базовом классе не тривиальный деструктор и в наследуемом, то как нужно определить деструктор, ведь если мы переопределим деструктор, то базовая не часть утечет

  • @viacheslavbarkov6538
    @viacheslavbarkov6538 2 ปีที่แล้ว

    1:35:21, слайд 53, наверно имелось в виду struct Derived : BaseNVI
    Ну и можно было сделать foo_impl protected в обоих классах

    • @tilir
      @tilir  2 ปีที่แล้ว

      Да действительно, досадная опечатка. А зачем protected?

    • @viacheslavbarkov6538
      @viacheslavbarkov6538 2 ปีที่แล้ว

      @@tilir это уже больше к красоте. Изменение уровня доступа при переопределении на мой взгляд не очень хорошая практика. Например в языках Java и C# она не разрешена, как и приватные виртуальные методы.
      Знаю людей, которые при реализации интерфейса, делают все методы приватными, чтобы можно было только через интерфейс вызывать

  • @wildboar5565
    @wildboar5565 8 หลายเดือนก่อน

    Спасибо за лекции! Правда я не до конца понял зачем нужны пустые классы и зачем EBCO и в каких случаях это используется🙁

    • @wildboar5565
      @wildboar5565 8 หลายเดือนก่อน +1

      Стоило мне попробовать поиграть с этим - вопрос разрешился сам собой. Гениальный эксплоит

    • @poigrushkin9433
      @poigrushkin9433 7 หลายเดือนก่อน

      ​@@wildboar5565харош, жги

  • @stanislavstanislavius7618
    @stanislavstanislavius7618 2 หลายเดือนก่อน

    Контест по си накрылся( ну, хоть успел все прорешать, кроме пары первых заданий из первого семинара

  • @Ii_Naomasa_san
    @Ii_Naomasa_san 2 ปีที่แล้ว +1

    Лекция супер! Но мне показалось, что не раскрыт вопрос: а когда использовать статический, а когда динамический полиморфизм? В каких случаях, что применять?

    • @tilir
      @tilir  2 ปีที่แล้ว +1

      Лекция про проектирование будет отдельно. Сейчас мы просто проходим средства языка. Кое-что о сценариях применения конечно говорится, но паззл пока не собран.

    • @Ii_Naomasa_san
      @Ii_Naomasa_san 2 ปีที่แล้ว

      @@tilir Ясно. Понятно). Может быть подскажите литературу, где можно найти ответ на этот вопрос. Был бы очень благодарен :)

    • @tilir
      @tilir  2 ปีที่แล้ว +2

      @@Ii_Naomasa_san мне из последних понравилась книга Пикуса "Hands-On Design Patterns with C++" (есть в русском переводе, издана в этом году). Всё очень по делу.

    • @SeriousMan212
      @SeriousMan212 5 หลายเดือนก่อน

      ​@@tilir О Пикус. Крутой мужик. Несколько раз бывал у него на публичных выступлениях. У него всегда с собой заготовочки нескольких строк кода и бенчей, через которые открываются двери в "Нарнию" =)

  • @xsomniator
    @xsomniator 2 ปีที่แล้ว

    Можем ли мы добавить в список примеров полиморфизма (перегрузка, шаблоны, виртуальные методы) класс variant с функцией visitor? Это полиморфизм в рантайме по типу хранимого значения, начиная с C++17 является частью стандарта.

    • @tilir
      @tilir  2 ปีที่แล้ว

      std::visit это довольно обычный compile-time полиморфизм. Там нет стирания типов и нет необходимости ничего решать в рантайме.

  • @alexskatkov2124
    @alexskatkov2124 2 ปีที่แล้ว

    Добрый день!
    Когда станет открытым курс по компиляторостроению? Только из-за вас заинтересовался этой темой. В хорошем смысле, конечно! Спасибо! Читаю "умные книжки", но мне этого не хватает. Хочу когда-нибудь увидеть ваши лекции и задания по курсу. Может быть, можно где-то посмотреть задание для компилятора ParaCL? Что-то вроде методы?

    • @tilir
      @tilir  2 ปีที่แล้ว

      Курс по компиляторам пока не сделан. Очень надеюсь что в ближайшие годы я к нему хотя бы приступлю. Мне есть что сказать на эту тему. Пока что всё, что я читаю, это несколько небольших лекций: ParaCL, clang, LLVM, toolchain, вот это всё.

    • @alexskatkov2124
      @alexskatkov2124 2 ปีที่แล้ว

      @@tilir То есть ребята пишут интерпретатор ParaCL со знаниями спецкурса по тулчейнам и курса бакалавров, где некоторые лекции поднимают в какой-то форме вопросы по парсингу, автоматам и всему такому?

    • @tilir
      @tilir  2 ปีที่แล้ว

      Нет, они пишут интерпретатор со знаниями в объёме iLab второго курса, который я сейчас и выкладываю.

  • @d7d1cd
    @d7d1cd 2 ปีที่แล้ว

    Слайд 63.
    Хотелось бы поумничать ). Если Part - пустой класс, то при композиции размер Whole будет больше, чем при наследовании.

    • @tilir
      @tilir  2 ปีที่แล้ว

      EBCO тоже разбирается на этой лекции. То есть если он пустой все знают что делать.

  • @user-b73mpf5uq3
    @user-b73mpf5uq3 2 ปีที่แล้ว

    19:45 Есть такой простенький вопрос.
    Насколько корректно в качестве напоминания, писать вот так:
    struct BinOp
    :
    public Node
    {
    /////
    };
    Идея в том, чтобы подчеркнуть сказанное (это как бы поле, которое лежит над структурой производного класса).
    Конкретно, вопрос в том - какова удобочитаемость?
    Не сильно ужасно, если я придерживаюсь такого стиля в некоторых местах?

    • @tilir
      @tilir  2 ปีที่แล้ว

      Мне кажется, что наследование в строчку это классика и по умолчанию читатель кода будет искать базовые классы справа по коду, а не снизу. Это сложившаяся норма, ломать её -- себе дороже.
      Я лично эти проблемы решаю так. Студенты пишут как угодно, а я, когда ко мне приходят их проекты, прогоняю их через индентацию (например clang-format) с дефолтными настройками. Убирая цветущую сложность и уникальные идеи по форматированию, но -- только на своей стороне, не вмешиваясь в творческий процесс и не выламывая руки.
      Так и вы. Выставите под ваш стиль настройки индента и редактируйте в таком стиле у себя, если вам так удобнее, никто и слова не скажет. Собственно никто и не узнает =)

    • @user-b73mpf5uq3
      @user-b73mpf5uq3 2 ปีที่แล้ว +1

      @@tilir хорошо, спасибо.
      И спасибо за хорошие лекции! Очень вкусная пища для мозга)

  • @ДмитрийАндреев-ф3х
    @ДмитрийАндреев-ф3х 6 หลายเดือนก่อน

    это сложно

  • @MVZ1983
    @MVZ1983 4 หลายเดือนก่อน

    да... хак на хаке и хаком погоняет
    хороший курс лекций, но как с этим жить?

  • @vuniverserb1142
    @vuniverserb1142 2 ปีที่แล้ว +1

    Доброго времени суток. "Агрегатная иницилизация работает если все поля в одной секции" на ~21 минуте. godbolt - z/h5sGqYcM4 У меня при приватной части ошибку выдает, хотя от туда от куда инициализирую права доступа есть. Заранее спасибо!

    • @tilir
      @tilir  2 ปีที่แล้ว

      Это меня что-то проглючило. По [dcl.init.aggr] An aggregate is an array or a class with [...] no private or protected direct non-static data members кроме всего прочего. Но я даже не могу внести это в errata так как лекция не про это и того нет на слайдах. Тем не менее спасибо за внимательность.

    • @vuniverserb1142
      @vuniverserb1142 2 ปีที่แล้ว

      @@tilir Спасибо