Использование вызова виртуальной функции разыменовывает объект

У меня есть указатель базового класса, указывающий на объект производного класса. Я звоню foo() функция при помощи двух различных путей в коде ниже. Почему делает Derived::foo() позвониться в первом случае? Не был должен (*obj).foo() звонить Base::foo() функционируйте, поскольку это было уже разыменовано?

    class Base
    {
    public:
        Base() {}
        virtual void foo() { std::cout << "Base::foo() called" << std::endl; }
        virtual ~Base() {};
    };

    class Derived: public Base
    {
    public:
        Derived() : Base() {}
        virtual void foo() {  std::cout << "Derived::foo() called" << std::endl; }
        virtual ~Derived() {};
    };

    int main() {
        Base* obj = new Derived();
   // SCENARIO 1
        (*obj).foo();
// SCENARIO 2
        Base obj1 = *obj;
        obj1.foo();

        return 0;
    }
6
задан Barracuda 3 February 2015 в 20:59
поделиться

8 ответов

 // СЦЕНАРИЙ 1
(* объект) .foo ();

Обратите внимание, что

  1. obj здесь неправильно, поскольку он относится не к объекту, а к указателю ,
  2. (* ptr) .foo () - это просто обходной способ выполнить ptr-> foo () .

* ptr приводит не к объекту, а к ссылке Base & на объект. И вызов виртуальной функции через ссылку подлежит динамической диспетчеризации, точно так же, как такой вызов через указатель.

 // СЦЕНАРИЙ 2
База obj1 = * ptr;
obj1.foo ();

Здесь вы создаете совершенно новый объект с помощью нарезки : у него просто есть части базового класса * ptr . Вместо этого вам нужно следующее:

Base& ref = *ptr;
ref.foo();
16
ответ дан 8 December 2019 в 05:53
поделиться

Это поможет, если вы немного подумаете о реализации. Во втором сценарии вы фактически создаете новый объект типа Base, который будет поставляться с новой таблицей виртуальных функций. Но в первом сценарии * obj будет «указывать на» или, скорее, ссылаться на объект, который все еще имеет таблицу виртуальных функций объекта типа Derived.

1
ответ дан 8 December 2019 в 05:53
поделиться

В дополнение к другим ответам.

Технический термин для описания того, что происходит в вашем Сценарии 2, - это нарезка объектов.

Вот запись в Википедии:

http://en.wikipedia.org/wiki/Object_slicing

А вот еще один вопрос о stackoverflow при нарезке объектов:

Что такое нарезка объектов?

1
ответ дан 8 December 2019 в 05:53
поделиться

В первом случае производная версия foo () будет вызываться по очевидным причинам, объясненным выше. В дополнение к другим ответам * (* Obj) .func () * является синонимом * Obj-> func () * .

Во втором случае создается экземпляр нового объекта класса Base через конструктор копирования, и, поскольку это объект класса Base , он вызовет класс Base . версия foo () .

1
ответ дан 8 December 2019 в 05:53
поделиться

Что вы подразумеваете под «поскольку он уже был разыменован»?

Указатель базового класса obj указывает на объект производного класса, и, поскольку вы объявили функцию foo () виртуальной, производный класс foo () будет называется.

0
ответ дан 8 December 2019 в 05:53
поделиться

Сценарий 2 создает совершенно новый объект типа Base. Таким образом, когда мы выполняем obj1.foo(), объект вообще не является Derived; мы никак не сможем вызвать функцию Derived.

В сценарии 1, однако, объект на самом деле является экземпляром Derived, к которому мы обращаемся через указатель Base. Это именно та ситуация, для которой предназначены виртуальные функции; используется реализация производного класса.

3
ответ дан 8 December 2019 в 05:53
поделиться

(Довольно странный вопрос. Скорее всего, кто-нибудь спросит, почему во втором случае Derived :: foo не вызывается.)

В языке C ++, какая версия виртуальной функции вызывается, полностью не зависит от того, что было, а что не было «разыменовано». Разыменование не имеет никакого значения. Единственное, что имеет значение, - это динамический тип объекта, используемого в вызове.

В первом случае вызывается Derived :: foo , потому что динамический тип объекта * obj - Derived .

Во втором случае динамический тип obj1 - Base , поэтому вызывается Base :: foo .

Другими словами, все работает как положено. Это заставляет задуматься о том, что заставило вас задать свой вопрос. Что заставило вас ожидать чего-то другого?

0
ответ дан 8 December 2019 в 05:53
поделиться

Полиморфизм работает со ссылками (результатом разыменования указателя) так же, как с указателями.

0
ответ дан 8 December 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: