Почему указатель базового класса указывает на чистый виртуальный метод в базовом классе, а не на скрытый метод в производном классе? - PullRequest
1 голос
/ 06 августа 2020
#include <iostream>

class A
{
    public:
        virtual ~A() = default;
        virtual void foo(void) = 0;
};

class B : public A
{
    private:
        int x;

    public:
        B(int a) : x(a) {}
        void foo(void) { std::cout << "B: " << x << "\n"; }
};

class Foo
{
    private:
        A* a_ptr;

    public:
        Foo (B& x) { a_ptr = &x; }
        A* get_ptr(void) { return a_ptr; }
        void dummy(void) { std::cout << "Foo: "; std::cout << a_ptr << "\t "<< typeid(*a_ptr).name() << "\n"; a_ptr->foo(); std::cout << "\n"; }
};

int main(void)
{
        B b(10);
        Foo f(b);

        f.dummy();
        return 0;
}

Если конструктор Foo принимает ссылку на объект B, тогда эта программа выполняется так, как я ожидал, т.е. a_ptr->foo() вызывает B::foo().

Однако, если конструктор изменяется так, чтобы параметр принимался по значению, то a_ptr->foo() преобразуется в A::foo() и приводит к pure virtual method called exception

Пример вывода (передан по ссылке :):

Foo: 0x7fffe90a24e0      1B
B: 10

Пример вывода (передан по значению):

Foo: 0x7fffc6bbab20      1A
pure virtual method called
terminate called without an active exception
Aborted (core dumped)

У меня смутное представление о том, почему это может происходить, и я ищу литературу или ссылку, которые могут доказать или опровергнуть мою гипотезу: при передаче по ссылке указатель базового класса a_ptr указывает на сущность, время жизни которой превышает время обращения к a_ptr->foo().

Однако при передаче по значению a_ptr указывает на временное, которое теряется при выходе из конструктора.

Я полагаю, это как-то связано с VTABLE из A, но я не могу понять это.

Ответы [ 2 ]

5 голосов
/ 07 августа 2020

Да, ваше подозрение верно.

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

Итак, вызов a_ptr->foo() в Foo::dummy() на самом деле является неопределенным поведением , поскольку a_ptr даже не указывает на действительный объект. Но, на самом деле, это не sh, поскольку A::foo() ни для чего не использует свой указатель this. Он просто указывает на определенную компилятором функцию, которая выдает ошибку pure virtual method called, которую вы не поймаете, поэтому ваша программа завершается.

2 голосов
/ 07 августа 2020

Вы назначили временный объект B ссылкой на a_ptr, который имеет тип A *. При выходе из конструктора этот временный объект уничтожается. Поскольку VTABLE также был уничтожен, он называется A :: foo, который является чисто виртуальным. Итак, вы поняли.

...