Не так чтоб каждый день, но иногда всё же требуется получить доступ к protected методам какого-то класса. Простейший пример — QPlainTextEdit. Сразу несколько методов, непонятно для чего сделанных защищёнными (тот же firstVisibleBlock(), например). Конечно, в теории следует наследоваться от базового класса, и таким образом получить доступ. Но не всегда есть возможность это сделать (если, скажем, базовый класс создаётся в какой-то части системы, которая ничего не знает о классе-наследнике), да и не всегда нужно. На днях у меня как раз возникла такая ситуация, поэтому спешу поделиться решением (найденным в интернете, честно говоря, но несколько доработанным).
Итак, простейший случай: метод не перегружен. Тут всё более-менее просто. Смотрим пример:
Теперь представим, что метод A::f() перегружен:
Итак, простейший случай: метод не перегружен. Тут всё более-менее просто. Смотрим пример:
class A
{
protected:
void f() {}
};
class B
{
public:
void g()
{
A a;
//делаем что-то с экземпляром A
//а вот теперь небольшой фокус
struct AHack : A
{
using A::f;
static void fHack(A *a)
{
if (!a)
return;
(a->*&AHack::f)();
}
};
//теперь вызываем защищённый метод
AHack::fHack(&a);
//продолжаем работу
}
};
Вот такой трюк с приведением типов. Хоть мы и наследовались от базового класса, но при вызове метода не создавали новых экземпляров.Теперь представим, что метод A::f() перегружен:
class A
{
protected:
void f() {}
void f(int) {}
};
Тут компилятор выдаст приблизительно такую ошибку: ... ошибка: '& A::f' cannot be used as a member pointer, since it is of type '<unresolved overloaded function type>'
Что делать? Как говорится, "we need to go deeper" (кто не в курсе — погуглите). Слегка изменим нашу структуру AHack: struct AHack : A
{
//using больше не понадобится
static void fHack(A *a)
{
if (!a)
return;
reinterpret_cast<AHack *>(a)->f();
}
static void fHack(A *a, int i)
{
if (!a)
return;
reinterpret_cast<AHack *>(a)->f(i);
}
};
Такой способ является универсальным и будет работать как в случае с перегруженным методом, так и в случае с неперегруженным. Теперь можно вызывать метод A::f() в обоих вариантах: AHack::fHack(&a);
AHack::fHack(&a, 10);
К reinterpret_cast обычно прибегать не рекомендуется, так как малейшая ошибка запросто может привести к падению программы, а найти эту ошибку будет довольно сложно. Если бы мы привели тип A * к типу AHack * и использовали бы какой-то метод, отсутствующий в A, то с большой долей вероятности программа бы упала. Но поскольку мы обращаемся только к методам A, то такое приведение типов можно назвать безопасным (но только в данной ситуации). Подробнее о приведении типов можно почитать в одном весьма и весьма интересном блоге, а у меня на сегодня всё.