Вызов виртуальной функции с использованием объекта разыменования - PullRequest
6 голосов
/ 21 июня 2010

У меня есть указатель базового класса, указывающий на объект производного класса. Я вызываю функцию 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;
    }

Ответы [ 8 ]

16 голосов
/ 21 июня 2010
// SCENARIO 1
(*obj).foo();

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

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

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

// SCENARIO 2
Base obj1 = *ptr;
obj1.foo();

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

Base& ref = *ptr;
ref.foo();
3 голосов
/ 21 июня 2010

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

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

1 голос
/ 21 июня 2010

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

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

1 голос
/ 21 июня 2010

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

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

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

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

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

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

1 голос
/ 21 июня 2010

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

0 голосов
/ 21 июня 2010

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

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

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

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

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

0 голосов
/ 21 июня 2010

Что вы подразумеваете под "как это уже разыменовано"?

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

0 голосов
/ 21 июня 2010

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

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