Pull to refresh

Comments 25

Давайте еще скажем, что кто-то не знал, что компилятору с -O3 или -Ofast вообще всё равно на ООП.

MSVC — очень даже не всё равно, увы. Хотя может в последних версиях поправили, но до 2010й версии можно было только через __forceinline убедить от всего этого избавляться.
#define protected public
# include <external_header>
#undef protected
UFO just landed and posted this here
Интересный вариант! Причем и для private-членов должно сработать.
Но есть один недостаток: то для «взлома» мне нужно было самому осознанно писать имя макроса, а ваше делает видимым вообще все и без макросов.
Мне все же больше понравился вариант с using.
Есть еще проще метод, многие компиляторы разрешают взятие адреса для методов не проверяя атрибуты доступа, так что можно еще и так.
Увы, многие компиляторы следуют стандарту

auto x = &A::f;

test.cpp(11): error C2248: 'A::f': cannot access protected member declared in class 'A'

Тогда еще можно встроенный ассемблер призвать на помощь, ну и еще немного разврату — мы люди без комплексов, можем и знаем толк как потрогать неприкасаемое так что б приятно компилируемо было.


#include <iostream>

using namespace std;

class A 
{
    int t;
    void printT()
    {
        cout<<"t="<<t<<endl;
    };
    int alpha() 
    {
        cout<<this<<"->alpha()"<<endl;
        this->printT();
        return 0;
    }
    public:
            A():t(0){;} 
            void setT(int _){this->t = _;}          
};

class B
{
    virtual int beta()
    {
        cout<<this<<"->beta()"<<endl;
        return 0;
    }
    public:
        virtual void gamma(){};
};

class C:public B
{
    public:
        int beta(){return 0;};
    private:
        void gamma(){cout<<"Private function of class C "<<this<<"->gamma() called"<<endl;}
};

int main()
{
    A e;
    e.setT(800);
    __asm 
    {
        lea edx, A::alpha       
        lea ecx, e
        call edx
    }
    C* e2 = (C*)new B;
    e2->beta();
    delete e2;
    B* e3 = new C;
    e3->gamma();
    delete e3;
    return 0;
}

Это в студии 2008 работало.

Да, так и в 2015-й работает. Внутри __asm не проверяется уровень доступа. Но, решение x86-only, а я уже весь софт таргетирую под x64, без вариантов.

Если в классе есть хоть один шаблонный метод, то можно и private-члены вытащить, написав его специализацию для какого-нибудь dummy-типа. Выйдет еще "красивее".

Другой вариант с наследованием:

class A
{
protected:
    void f() {}
};

class B: public A
{
public:
    A::f;
};

int main()
{
    B b;
    b.f();
    return 0; 
}
Не обязательно задавать параметры метода, можно сделать проще с помощью using.

class A
{
protected:
	int f() { return 0; }
};
class B : public A
{
public:
	using A::f;
};

A a;
static_cast<B&>(a).f();
Действительно, забыл про эту фишку. Спасибо!
class B: public A {
public: using A::f;
};


Такое преобразование является неопределённым поведением. Даже если прямо сейчас это работает, нет никакой гарантии, что поведение не изменится в самом ближайшем будущем.
Подробнее про то, почему это UB можно посмотреть на stackoverflow (теория)(похожий пример)
Кажется, есть простое решение, вообще не использующее грубых приведений типов.
Работает вроде бы в любом компиляторе, C++11 тоже не требуется.
static int _f(A &a){ return (a.*&B::f)(); }


В студии работает и A::f вместо B::f.
В студии работает и A::f вместо B::f.
Проверил в 2015-й:
static int _f(A &a){ return (a.*&A::f)(); }
test.cpp(8): error C2248: 'A::f': cannot access protected member declared in class 'A'

зы. А, этот static int _f нужно поместить внутрь класса B. Но от создания лишнего класса не уходим ((
Ну да, это поправка к тому, что в статье называется Идея 1. То есть дополнительный класс B со статическим методом, но без каких-либо грубых преобразований типов.

А по этой ссылке можно найти (более извращенный) способ достучаться и до приватных полей и функций. Там еще в комментариях полезные ссылки.
Если у Вас метод protected, зачем все так сложно? Почему нельзя унаследоваться от A и работать уже с классом-наследником?
Работа с целевым классом в используемой мной библиотеке ведется не напрямую, а через еще один класс.
Потоки из стандартной библиотеки устроены таким образом, что сначала реализуется наследник std::streambuf, а потом он используется в другом классе, наследующем std::iostream. Чтобы правильно внедрить свой streambuf, пришлось бы разбираться еще и в классе-наследнике iostream.
Если знаете способ обойти это, буду признателен за совет.
Вы можете описать задачу более конкретно?
Потому что в статье ни слова про такие трудности (не считая одного упоминания iostream). В статье Вы просто пишите, что метод, возвращающий файловый дескриптор находится в секции protected.
Попытался построить простую модель решаемой задачи:

Код
class TBase {};

class Target : public TBase {
	protected: int target_function(){ return 5; }
};

class Base {
	private: TBase *tbase;
	public: Base(TBase *t) : tbase(t) {}
};

class Main : public Base {
	private: Target t;
	public:
		Main() : Base(&t) {}
		const Target &getTarget(){ return t; }
};

int main(){
	Main m;
	int val = m.getTarget().target_function(); //нужно получить это
}

Реальные классы можете посмотреть в коде библиотеки. Для решения задачи наследованием нужно внедрить в класс basic_pstream своего наследника basic_pstreambuf.
Sign up to leave a comment.

Articles