Вызовется ли виртуальная функция дочернего класса по указателю на функцию родительского?
Псевдокод:
class A
{
public:
virtual void foo()
{
}
};
class B : public A
{
public:
virtual void foo()
{
}
};
A a;
void * pFunction = &а::foo;
// приводим функцию к нужному типу и вызываем (как это записать правильно? )
*(( А::foo* )pFunction )();
// Вызовется ли здесь B::foo() ? Если нет, то как вызвать B::foo(), имея адрес A::foo() ?
Или это вообще ужос-ужос-ужос и красиво все надо писать не так?
Здравствуйте, Аноним, Вы писали:
А>Вызовется ли виртуальная функция дочернего класса по указателю на функцию родительского?
А>Псевдокод:
А>
А>class A
А>{
А> public:
А> virtual void foo()
А> {
А> }
А>};
А>class B : public A
А>{
А> public:
А> virtual void foo()
А> {
А> }
А>};
А>A a;
А>void * pFunction = &а::foo;
А>// приводим функцию к нужному типу и вызываем (как это записать правильно? )
А>*(( А::foo* )pFunction )();
А>// Вызовется ли здесь B::foo() ? Если нет, то как вызвать B::foo(), имея адрес A::foo() ?
А>
А>Или это вообще ужос-ужос-ужос и красиво все надо писать не так?
http://www.rsdn.ru/article/cpp/fastdelegate.xmlАвтор(ы): Don Clugston
Дата: 27.07.2005
В данной статье предоставлен исчерпывающий материал по указателям на функции-члены, а также приведена реализация делегатов, которые занимают всего две операции на ассемблере.
Base* pBase = new Derived;
void (Base::*pMyFuncPtr)() = &Base::Func;
(pBase->*pMyFuncPtr)();
Здравствуйте, Аноним, Вы писали:
А>Вызовется ли виртуальная функция дочернего класса по указателю на функцию родительского?
Да.
Смысл в том, грубо говоря, что семантически вызов указуемой функции должен быть таким же, как если имя этой функции подставить по месту.
http://ideone.com/QtwoEs
#include <cstdio>
struct A
{
virtual void foo() { puts("fa"); }
void goo() { puts("ga"); }
};
struct B: A
{
virtual void foo() { puts("fb"); }
void hoo() { puts("hb"); }
};
typedef void (A::*FA)();
typedef void (B::*FB)();
void testA(FA f)
{
A a; (a.*f)();
B b; (b.*f)(); // ковариантность левого операнда
}
void testB(FB f)
{
//A a; (a.*f)(); // ошибка компиляции! а вдруг справа функция, специфичная для B (hoo, например)
B b; (b.*f)();
}
int main()
{
testA(&A::foo); // fa, fb
testA(&A::goo); // ga, ga - потому что невиртуальная функция
//testA(&B::hoo); // ошибка компиляции: а вдруг внутри слева объект A? (и он там, действительно, есть)
//testA(&B::foo); // ошибка компиляции: ну и что, что это переопределение, тип-то уже приподнят
testB(&A::foo); // fb
testB(&A::goo); // ga
testB(&B::foo); // fb
testB(&B::hoo); // hb
}
А>Или это вообще ужос-ужос-ужос и красиво все надо писать не так?
Указатели на члены — это недоуказатели на функции, с корявым синтаксисом и неочевидной для новичка ковариантностью типов по своему значению (fb = &A::foo) и контравариантностью — по левому аргументу (объекту). Тогда как у обычных указателей — контравариантность по значению: A* pa = pb.
Поэтому, если не хочется ломать себе голову, и если не нужно приведение типов из коробки, то лучше использовать свободные функции, стандартные замыкания и лямбды.
// в сишном стиле
void a_foo(A* a) { a->foo(); }
a_foo(&b);
// C++98-03
boost::bind(&A::foo, _1) (&b);
boost::function<void(A*)> a_foo = boost::bind(&A::foo, _1);
a_foo(&b);
// C++11
auto a_foo_1 = std::bind(&A::foo, _1);
std::function<void(A*)> a_foo_2 = a_foo;
auto a_foo_3 = [](A* a) { a->foo(); }
Синтаксис тоже страшненький, но, по крайней мере, семантика предсказуема, а главное, что она унифицирована с любыми другими свободными функциями.