Виртуальные функции-члены и std :: tr1 :: function: как это работает? - PullRequest
3 голосов
/ 09 сентября 2010

Вот пример кода. Обратите внимание, что B является подклассом A, и оба предоставляют уникальную подпрограмму print. Также обратите внимание, что в main оба bind вызова относятся к &A::print, хотя в последнем случае передается ссылка на B.

#include <iostream>
#include <tr1/functional>

struct A
{
    virtual void print()
    {
        std::cerr << "A" << std::endl;
    }
};

struct B : public A
{
    virtual void print()
    {
        std::cerr << "B" << std::endl;
    }
};

int main (int argc, char * const argv[])
{
    typedef std::tr1::function<void ()> proc_t;

    A a;
    B b;

    proc_t a_print = std::tr1::bind(&A::print, std::tr1::ref(a));
    proc_t b_print = std::tr1::bind(&A::print, std::tr1::ref(b));

    a_print();
    b_print();

    return 0;
}

Вот вывод, который я вижу при компиляции с GCC 4.2:

A
B

Я бы посчитал это правильным поведением, но затрудняюсь объяснить, как оно работает должным образом, учитывая, что std::tr1::function s были связаны с &A::print в обоих случаях. Может кто-нибудь, пожалуйста, просветите меня?

РЕДАКТИРОВАТЬ: Спасибо за ответы. Я знаком с наследованием и полиморфными типами. Что меня интересует, так это то, что &A::print означает ? Является ли это смещением в vtable, и этот vtable изменяется в зависимости от упомянутого объекта (в данном случае a или b?) С точки зрения более простого понимания, как этот код работает правильно? *

Ответы [ 2 ]

5 голосов
/ 09 сентября 2010

Это работает так же, как и с обычными указателями на функции-члены.Следующее выдает тот же результат:

int main ()
{
    A a;
    B b;
    typedef void (A::*fp)();
    fp p = &A::print;
    (a.*p)(); // prints A
    (b.*p)(); // prints B
}

Было бы удивительно, если boost / tr1 / std :: function сделал что-то другое, поскольку они предположительно сохраняли эти указатели на функции-члены под капотом.Да, и, конечно же, упоминание этих указателей не является полным без ссылки на статью быстрых делегатов .

2 голосов
/ 09 сентября 2010

Поскольку print() объявлено virtual, A является полиморфным классом. Привязав указатель к функции print, вы будете вызывать указатель A, почти так же как:

A* ab = &b;
ab->print();

В приведенном выше вызове ->print можно ожидать полиморфного поведения. То же самое верно и в вашем коде. И это хорошо, если вы спросите меня. По крайней мере, большую часть времени. :)

...