Подтвердить что ты не робот

Является ли указатель на действительную функцию-член действительной в конструкторе базового класса?

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

Учитывая следующее

class A
{
    void (A::*m_pMember)();

public:
    A() :
        m_pMember(&A::vmember)
    {
    }

    virtual void vmember()
    {
        printf("In A::vmember()\n");
    }

    void test()
    {
        (this->*m_pMember)();
    }
};

class B : public A
{
public:
    virtual void vmember()
    {
        printf("In B::vmember()\n");
    }
};

int main()
{
    B b;
    b.test();

    return 0;
}

Будет ли это производить "In B:: vmember()" для всех совместимых компиляторов С++?

4b9b3361

Ответ 1

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

Обратите внимание также, что указатели на функции-члены обычно не привязаны к определенным функциям в момент инициализации. Если целевая функция не является виртуальной, можно сказать, что указатель указывает на определенную функцию. Однако, если целевая функция является виртуальной, нет способа сказать, куда указывает указатель. Например, в спецификации языка явно указано, что при сравнении (для равенства) двух указателей, которые указывают на виртуальные функции, результат не указан.

Ответ 2

"Действительный" - это особый термин применительно к указателям. Указатели данных действительны, когда они указывают на объект или NULL; указатели функций действительны, когда они указывают на функцию или NULL, а указатели на элементы действительны, когда точка для члена или NULL.

Однако из вашего вопроса о фактическом выходе я могу сделать вывод, что вы хотели спросить что-то еще. Посмотрите на свою функцию vmember - или я должен сказать, что функции? Очевидно, что существуют два тела функций. Вы могли бы сделать только производную одну виртуальную, так что это тоже подтверждает, что на самом деле есть две функции vmember, которые оба являются виртуальными.

Теперь возникает вопрос, будет ли при выборе адреса функции-члена выбрать фактическую функцию. Ваши реализации показывают, что они этого не делают, и что это происходит только тогда, когда указатель фактически разыменован.

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

namespace {
  void (A::*test)() = &A::vmember;
  A a;
  B b;
  (a.*test)();
  (b.*test)();
}

Когда мы инициализируем test, объект типа A или B вообще не существует, но все же можно взять адрес &A::vmember. Этот же указатель-член может быть использован с двумя разными объектами. Что это может произойти, но "В A:: vmember()\n" и "In B:: vmember()\n"?

Ответ 3

Прочтите эту статью для углубленного обсуждения указателей на функции-члены и их использования. Это должно ответить на все ваши вопросы.

Ответ 4

Я нашел небольшое объяснение Old New Thing (блог Раймонда Чена, иногда называемый Microsoft Chuck Norris).

Конечно, он ничего не говорит о соблюдении, но объясняет, почему:

B b;

b.A::vmember(); // [1]

(b.*&A::vmember)(); // [2]

1 и 2 действительно ссылаются на другую функцию... что действительно удивительно. Это также означает, что вы не можете фактически предотвратить отправку времени выполнения с помощью указателя на функцию-член:/

Ответ 5

Думаю, нет. Указатель на функцию виртуального члена разрешен через VMT, так же, как и вызов этой функции. Это означает, что это неверно, поскольку VMT заполняется после завершения конструктора.

Ответ 6

ИМО это implementation defined, чтобы принять адрес виртуальной функции. Это связано с тем, что виртуальные функции реализуются с использованием vtables, специфичных для реализации компилятора. Поскольку vtable не гарантируется завершение до выполнения класса ctor, указатель на запись в такой таблице (виртуальная функция) может быть implementation defined behavior.

Есть несколько смежный вопрос, который я задал здесь SO здесь несколько месяцев назад; который в основном говорит, что прием адреса виртуальной функции не указан в стандарте С++.

Итак, в любом случае, даже если это сработает для вас, решение не будет переносимым.