1. C++ / Говнокод #22704

    −125

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    #include <iostream>
    
    class Base {
    public:
        void publicThingy() const {
            std::cout << "Base::publicThingy()\n";
            privateThingy();
        }
    
    private:
        virtual void privateThingy() const {
            std::cout << "Base::privateThingy()\n";
        }
    };
    
    class Derived : public Base {
    private:
        virtual void privateThingy() const {
            std::cout << "Derived::privateThingy()\n";
        }
    };
    
    int main() {
        const Base &b = Derived();
        b.publicThingy();
    }

    Для любителей покритиковать костыли в крестах. Еще один повод: vtable'y до лампочки на private/public.
    https://wandbox.org/permlink/tAjx2MKozsbJ8QyT

    Запостил: Elvenfighter, 29 Марта 2017

    Комментарии (18) RSS

    • Disclaimer: я спациально написал `virtual` вместо `override` в Derived чтоб подчеркнуть нелепость ситуации.
      Ответить
      • я честно говоря не знаю что ты ожидал. по линку, там печатается как и ожидается "Base::publicThingy()" и потом "Derived::privateThingy()"

        > Еще один повод: vtable'y до лампочки на private/public.

        публик/приват это только для разрабов - на митингах громко полаятся, или в вершн контрол тихие войны вести. на кодогенерацию/рантайм/этц имеет почти нулевой эффект.
        Ответить
        • Ошибка доступа на этапе компиляции. Но, такой подход уже возвели в паттерны, по этому просто здесь плакаюсь.
          Ответить
    • Тоже мне откровение. google://Non-Virtual+Interface+Idiom
      Ответить
      • Именно. Оттуда и ноги растут (see what I did here? :))
        Ответить
        • если хочешь запретить переопределение метода, можно указать для него final. Но методы же для того и делают виртуальными, чтобы их можно было переопределять, не?

          п.с. private virtual всё-таки что-то из разряда "не надо так"
          Ответить
          • > п.с. private virtual всё-таки что-то из разряда "не надо так"

            Пишем на плюсах, а мейерса не читали, как так? Я всё время использую NVI.

            private управляет доступом, а не возможностью переопределять. Т.е. приватную виртуальную функцию переопределить можно, а вызвать -- нет. Это же киллер-фича, потому-что унылый protected толком ничего не скрывает.
            Ответить
            • во-первых, читал, во-вторых, проблема-то именно в том, что можно приватный виртуальный метод вынести в паблик:

              class Base
              {
              public:
                  void method() {
                      cout << "method started" << endl;
                      _impl();
                      cout << "method finished" << endl;
                  }
              private:
                  virtual void _impl() { cout << "Base" << endl; }
              };
              
              class Derived : public Base
              {
              public:
                  void _impl() override { cout << "Derived" << endl; }
              };
              
              int main(int, char **) {
                  Derived d;
                  d._impl();
              }

              А это так называемый fragile interface
              Ответить
              • > можно приватный виртуальный метод вынести в паблик

                Да, но не совсем так: можно переопределить И сделать публичным, просто опубликовать кишки базового класса нельзя. Тут уж пусть наследник пусть сам свои кишки защищает.
                С другой стороны да, если уж хочется прострелить себе ногу, способ всегда найдётся.
                Ответить
                • хорошо хоть
                  void _impl() override { Base""_impl(); }

                  нельзя. Хотя, при определении методов очень часто надо вызвать базовую реализацию. Поэтому опять же, NVI хрупок, а виртуальные методы лучше делать в public/protected
                  Ответить
                  • > Хотя, при определении методов очень часто надо вызвать базовую реализацию.

                    А вот и нет, NVI отчасти нужен для того, чтобы нельзя было забыть это сделать. Трёхуровневые иерархии нинужны.
                    Ответить
                    • там, где они не нужны, можно дописать к классу или методу final. В противном случае лучше проектировать классы так, чтобы их можно было расширять наследованием. Принцип Лисков, все дела
                      Ответить
            • Интересно, что в swift'е так сделать, например, нельзя (а там protected'а нет в принципе)
              Ответить
      • > http://www.blackwasp.co.uk/NVI.aspx

        я не уловил в чем фишка. там два обзаца из какой произвольной "ОО для чайников" слизано - и потом это названо паттерном.
        Ответить
        • > я не уловил в чем фишка

          Суть в том, чтобы считать наличие виртуальных функций скорее деталью реализации, чем частью интерфейса. Делаешь все публичные методы невиртуальными, все виртуальные — приватными. Так можно легко добавлять в базовый класс код, который должен быть общим для всех "потомков". Например:
          class Base {
            public:
              Status Init(Data data) {
                if (!IsValid(data)) {
                  return Status::InvalidArgument(ProblemDescription(data));
                }
                InitSelf(data); // setup base class properly
                return DoInit(data); // setup child properly
              }
            private:
              bool IsValid(Data data) const;
              void InitSelf(Data data);
          
              virtual Status DoInit(Data data) { return Status::Ok(); }
          };
          Так потомки не смогут забыть проверить данные и инициализировать базовый класс.

          > http://www.blackwasp.co.uk/NVI.aspx

          Лучше http://www.gotw.ca/publications/mill18.htm
          Ответить
          • уловил. но с точки зрения что производительности, что организиции это lose-lose решение: и вдвое больше методов (more boilerplate), и в конце все равно сводится к вызову виртуальных методов. (и толстые базовые классы - это часто свой отдельный ад.)

            на крестах, с его static binding, интерфейсы всегда были граблями обложены. (к слову, object slicing было первое что я подумал когда код в ГК увидел.) но как стандарт развивается, даже какой примитивной встроенной версии run-time binding или мессадж диспатч мы в крестах никогда недождёмся. на потомках smalltalk'а (за исключением жабы) проблем с интерфейсами редко наблюдается, т.к. интерфейсы слабые (более duck typing, чем компайл тайм чекинг), их проще менять в ходе жизни проекта, потому что нет такой жесткой привязки подклассов к базовому классу.
            Ответить
    • Ну ты автор КЭП, держи картонную медаль и баночку нутеллы.
      Ответить

    Добавить комментарий