Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Общие вопросы > Вызов приватного метода базового класса из дочернего


Автор: JoeUser 20.04.17, 23:36
Приветствую!

Как-то недавно где-то на форуме поднималась сабжевая тема. Захотелось поэксперементировать, вызвать напрямую, и таки получилось. Скажу сразу что реализация - есть хак. Иными словами, некоторые компиляторы могут собрать неработающий код. Вощем, опубликую чтобы было :lol:

Онлайн выполнение тут.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
     
    // -----------------------------------------
     
    class Base {
      public:
       virtual ~Base(){}
      private:
        virtual void ShowBase() {
          std::cout << "BaseShow" << std::endl;
        }
    };
     
    // -----------------------------------------
     
    class Derived: public Base {
      public:
        virtual void ShowDerived() {
          std::cout << "DerivedShow" << std::endl;
        }
    };
     
    // -----------------------------------------
     
    #ifdef _MSC_VER
      #define IDX 1
    #else
      #define IDX 2
    #endif
     
    int main() {
      Derived *D = new Derived();
      (*(void(*)(void*))((void**)(*(void**)D))[IDX])(D);  
      delete D;
      return 0;
    }

Автор: simsergey 21.04.17, 05:01
Т.е. вызывался приватный метод?
Какая прелесть.

Автор: Pacific 21.04.17, 05:36
Ой, делов-то:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #define private public
    #include "BaseClass.h"

:D

Автор: JoeUser 21.04.17, 06:19
Pacific, задача была вызвать приватный метод, а не делать его пабликом.

Автор: JoeUser 21.04.17, 07:39
На стековерфлове предложили еще один вариант:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
     
    class Private {
      void private_func() { std::cout << "Pwned!" << std::endl; }
    };
     
    class DerivedPrivate : public Private {
    };
     
    using PTR = void (Private::*)();
    PTR ptr;
     
    template <PTR ptr> struct Exploit {                
      static struct D { D() { ::ptr = ptr; } } d;
    };
     
    template <PTR ptr> typename Exploit<ptr>::D Exploit<ptr>::d;
     
    template struct Exploit<&DerivedPrivate::private_func>;
     
    int main() {
      (DerivedPrivate().*ptr)();
    }

Автор: Serpentus 21.04.17, 08:49
DELETED

Сначала, не подумав, решил, что достаточно сделать
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    static_cast<Base*>(this)
:D
Не, без тяжелых извращений к приватным членам класса доступ не получить. И не надо!

Автор: Qraizer 21.04.17, 12:31
JoeUser, чему ты удивляешься-то? Тому, что при наличии указателя на что-то можно с чем-то делать всё, минуя любые охранные средства языка? Дык не составляет труда получить доступ и к приватным мемберам. Я даже как-то код писал, который в ран-тайм распарсивает структуру с битовыми полями и составляет словарь из битовых смещений от начала и длин каждого такого поля. Это требовалось для тестового движка одного из проектов.
Структура VMT не стандартизирована, даже её наличия не требуется. Твой код написан под конкретную 32-битную реализацию – кстати, какую? – и нет никаких гарантий, что будет работать в других. Код со СтэкОверфлоу не страдает этим недостатком, но страдает другим: он не должен компилиться.

Добавлено
Но я могу предложить другой способ.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #include <iostream>
     
    class Private {
      void private_func() { std::cout << "Pwned!" << std::endl; }
     
    public:
      template <typename T> void f();
    };
     
    /* ... */
     
    class X {};
    void (Private::*ptr)();
     
    template <> void Private::f<X>()
    {
      ptr = &Private::private_func;
    }
     
    int main() {
       Private().f<X>();
      (Private().*ptr)();
    }

Автор: JoeUser 21.04.17, 13:03
Цитата Qraizer @
Твой код написан под конкретную 32-битную реализацию – кстати, какую?

Это почему?

Цитата Qraizer @
Но я могу предложить другой способ.

"Легальные" способы, в случае перегрузки, вызовут перегруженный метод, а не базового класса. А в твоем варианте как с этим?

Автор: JoeUser 21.04.17, 13:13
Цитата Qraizer @
32-битную реализацию – кстати, какую?

Собирал на FreeBSD 11.0 x64 - полет нормальный.
Цитата

uname -a
FreeBSD freebsd-11.0 11.0-RELEASE-p1 FreeBSD 11.0-RELEASE-p1 #1: Tue Oct 11 13:50:48 MSK 2016 majestio@freebsd-11.0:/usr/obj/usr/src/sys/MYKERNEL amd64

Автор: Qraizer 21.04.17, 13:16
Цитата JoeUser @
Это почему?
Уже нет, uint32_t* заменил на void**.
Цитата JoeUser @
А в твоем варианте как с этим?
Мой строго стандартен.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)