Действительно ли приведение работает, когда выполняется внутри вызова функции в C ++? - PullRequest
1 голос
/ 24 января 2012

Мой код:

#include <iostream>

using namespace std;

class A
{
public:
    virtual void print(void) { cout << "I am base class" << endl; }
};

class B : public A
{
public:
    void print(void) { cout << "I am class B" << endl; }
};

void mainprint(A *a)
{
    (*a).print();
}

int main()
{
    A a;
    B b;

    B *bp;
    A *ap;

    ap = &b;

    a.print();
    b.print();
    (*ap).print();

    bp = new B();

    mainprint((A *)bp);

    delete bp;

    return 0;
}

Вывод:

I am base class
I am class B
I am class B
I am class B

Я поместил указатель (bp) в класс A внутривызов функции, но он все еще вызывает производный класс print !!!

Может кто-нибудь пролить свет на это для меня.

Ответы [ 6 ]

6 голосов
/ 24 января 2012

Я привел указатель (bp) к классу A внутри вызова функции, но он все еще вызывает базовый класс print !!!

Полагаю, вы имеете в виду "вызывает производнуюclass print ", так как именно это и произошло.

В этом весь смысл виртуальных функций;Окончательное переопределение, связанное с фактическим типом объекта (т. е. «динамическим типом»), выбирается независимо от типа ссылки или указателя, используемого для вызова функции (т. е. «статического типа»).Поэтому B::print выбрано, потому что bp по-прежнему указывает на экземпляр B.

Если вы хотите принудительно вызвать A::print, вы можете сделать:

pb->A::print()

или, если вам вообще не нужно полиморфное поведение, удалите спецификацию virtual.

3 голосов
/ 24 января 2012

Вы явно запросили это поведение, установив A::print() a виртуальную функцию .

2 голосов
/ 24 января 2012

Это виртуальная функция диспетчеризации (то есть полиморфизм времени выполнения), работающая как задумано.

Один из способов отключить отправку виртуальной функции - указать имя функции, определяемое для подробностей, с помощью имени класса, например:

void mainprint(A *a) 
{ 
  (*a).A::print(); 
}
1 голос
/ 24 января 2012

Я не понимаю, в чем проблема ... первая - print для реального объекта типа A, все остальные - вызовы print над B объектом (напрямую или через указатель), поэтому он вызывает B '* print.

Имейте в виду, что при вызове функции вы не приводите объект типа B к объекту типа A (что приводит к нарезке), но вы просто указываете на него - сам объект остается неизменным, и благодаря виртуальной диспетчеризации, даже если статический тип объекта равен A *, вызываются правильные версии виртуальных функций.

Вот как работают virtual функции.

1 голос
/ 24 января 2012

Чего вы ожидали?Вот как работает полиморфизм.

Такое поведение ожидается и корректно.

Кроме того,

(*a).print();

- это не приведение, а разыменование.

0 голосов
/ 24 января 2012

Это не вызов print базового класса, это вызов производного класса print (я класс B) для вашего последнего вызова, который является правильным поведением, потому что указатели и ссылки работают с полиморфизмом.

ЕслиВы нарезали свой объект, затем вызвали печать, это действительно напечатало бы версию А.Вы не хотите этого делать, и, к счастью, вы этого не сделали.

Это будет нарезка:

B b;
A a(b); 
A a2;
a2 = b;
A& a3 = b;
b.print();
a.print();
a2.print();
a3.print();

, и вы должны получить:

Якласс BI - базовый класс. Я - базовый класс. Я - класс B

, поскольку a и a2 оба являются объектами типа a, даже если вы присвоили их b.Нарезка не рекомендуется, но это распространенная ошибка.

a3, однако, является ссылкой на объект типа A или одно из его производных, но на самом деле является объектом b типа B и сохраняет полиморфное поведение.

...