Вызов определения базового класса виртуальной функции-члена с указателем на функцию - PullRequest
19 голосов
/ 30 июля 2009

Я хочу вызвать реализацию базового класса виртуальной функции, используя указатель на функцию-член.

class Base {
public:
    virtual void func() { cout << "base" << endl; }
};

class Derived: public Base {
public:
    void func() { cout << "derived" << endl; }

    void callFunc()
    {
        void (Base::*fp)() = &Base::func;
        (this->*fp)(); // Derived::func will be called.
                       // In my application I store the pointer for later use,  
                       // so I can't simply do Base::func().
    }
};

В приведенном выше коде реализация производного класса будет вызываться из callFunc. Есть ли способ сохранить указатель на функцию-член, который указывает на Base :: func, или мне придется каким-то образом использовать using?

В моем реальном приложении я использую boost :: bind для создания объекта boost :: function в callFunc, который я позже использую для вызова func из другой части моей программы. Так что, если boost :: bind или boost :: function найдут способ обойти эту проблему, это также поможет.

Ответы [ 6 ]

12 голосов
/ 30 июля 2009

Когда вы вызываете виртуальный метод через ссылку или указатель, вы всегда активируете механизм виртуального вызова, который находит наиболее производный тип.

Лучше всего добавить альтернативную функцию, которая не является виртуальной.

2 голосов
/ 30 июля 2009

К сожалению, то, что вы пытаетесь сделать, невозможно. Функции указателя на член * предназначены для поддержки виртуальности указанной функции.

1 голос
/ 30 июля 2009

Ваша проблема в том, что указатель на функцию-член не совсем совпадает с указателем на функцию-голую. На самом деле это не просто указатель, а значительно более сложная структура , которая различается в деталях на уровне реализации компилятора. Когда вы вызываете его через синтаксис (this->*fp)(), вы фактически вызываете его для исходного объекта, что вызывает диспетчеризацию виртуальной функции.

Единственное, что может сработать, - привести его к типу указателя, не являющегося методом. Это немного скрипит, но я думаю это должно сработать. Вам все еще нужно передать Base *, но вы делаете это явно, и отправка виртуальной функции игнорируется:

typedef void BasePointer(Base*);

void callFunc()
{
    BasePointer fp = (BasePointer *)&Base::func;
    fp(this);
}

Обновление: Хорошо, нет, вы не можете сделать это таким образом. Это незаконно и не будет безопасно, если это будет законно. C ++ FAQ содержит больше об этом . Но зная, что это не решит вашу проблему. Проблема в том, что указатель на объект или указатель на член, если вы хотите вызвать Base::func через Base указатель, объект, на который он указывает, должен также быть Base. Если вы можете это организовать, то можете использовать указатель на функцию-член.

Вот еще одна мысль, не красивая, но, по крайней мере, осуществимая. Укажите в Derived функцию, не являющуюся виртуальной, которая явно вызывает Base::func. Вместо этого укажите на это. Он не будет масштабироваться, если вам нужно сделать это в общем случае множества различных вариантов func и callFunc, но он будет хорошо работать для одного метода.

0 голосов
/ 31 июля 2009

Что с этим не так?

(Base(*this).*fp)();

Теперь, если вас это устраивает, возникает вопрос о том, почему вы вообще используете указатель на функцию. Я думаю, что может помочь еще какой-то контекст.

0 голосов
/ 30 июля 2009

В дополнение к тому, что говорит кварк, более общим замечанием является то, что вы должны использовать реализацию сигнал / слот, а не пустой указатель на функцию. У Boost есть один, есть libsigc и куча других.

0 голосов
/ 30 июля 2009

Есть ли какая-либо конкретная причина для этого через указатель на функцию?

Вы должны просто написать:

Base::func();

для вызова реализации базового класса.

...