как работают виртуальные функции и что происходит с новым типом возвращаемого значения при назначении?

#include<iostream>
using namespace std;

class Father
{
public:
  int a=99;
  void MakeAThing(){ cout<<"MakeAThing of father called"<<endl;}
  virtual void MakeAThing2(){ cout<<"MakeAThing2 of father called"<<endl;}
};
class Child : public Father
{
public:
  int b=11;
  void MakeAThing(){ cout<<"MakeAThing of child called"<<endl;}
  virtual void MakeAThing2(){ cout<<"MakeAThing2 of child called"<<endl;}
};

int main(){

  Father *obj;

  obj = new Child();
  obj->MakeAThing();// MakeAThing of Father is called

  //code to find out if  obj becomes a Child* after new
  Father fa  
  fa=*obj;
  cout <<fa.b<<endl; // error: 'Class Father' has no member 'b'
  //which means fa remains an object of Class Father


  Father *obj2;
  obj2 = new Child();
  obj2->MakeAThing2();// MakeAThing2 of Child is called
return 1;
}

Кто-нибудь может объяснить, что происходит в главном? Моя попытка:

obj — указатель на объект типа «Отец».

В obj= new Child(); new выделяет память для объекта типа Child и возвращает указатель на void: *void, верно?, так obj преобразуется в тип *void или остается типом *Father? Зачем?

В obj->MakeAThing(); MakeATThing of Father называется, почему?

Почему в obj2->MakeAThing2(); ключевое слово виртуальный заставляет вызов MakeATThing2 относиться к ребенку, а не к отцу?


person J.C.VegaO    schedule 10.12.2019    source источник
comment
new Child() возвращает Child*, а не void*   -  person UnholySheep    schedule 11.12.2019
comment
почему ключевое слово virtual заставляет вызов MakeAThing2 ссылаться на вызов Child, а не на вызов отца? Потому что именно в этом и заключается цель virtual — предоставить производному классу способ переопределить функциональность базового класса.   -  person TheUndeadFish    schedule 11.12.2019
comment
@ UnholySheep, кажется, я читал, что new имеет возвращаемый тип void*. Если я сделаю new int, тип возвращаемого значения будет int*? так каждый раз меняется?   -  person J.C.VegaO    schedule 11.12.2019
comment
@juancarlosvegaoliver Кажется, я читал, что new has return type void* Где вы это прочитали? Вы путаете std::malloc с new?   -  person Algirdas Preidžius    schedule 11.12.2019
comment
@Algirdas Preidžius да, наверное, потому что я родом из C. Так зависит ли возвращаемый тип новой переменной от того, на какой тип она действует?   -  person J.C.VegaO    schedule 11.12.2019
comment
@juancarlosvegaoliver Об этом можно узнать, прочитав документацию новое выражение. Кроме того, чтобы внести путаницу, существует operator new, но он вызывается , за кулисами, по выражению new, которое вы, собственно, здесь и используете. Чтобы узнать о разнице между ними, вы можете дополнительно прочитать здесь: stackoverflow.com/questions/8962467/   -  person Algirdas Preidžius    schedule 11.12.2019


Ответы (1)


Статический тип обоих указателей — Father *.

Father *obj;
Father *obj2;

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

В результате в этом утверждении

obj->MakeAThing();

называется функцией-членом класса Отец.

В этом звонке

obj2->MakeAThing2();

компилятор вызовет переопределяющую виртуальную функцию, определенную в классе Child, за счет вызова функции с использованием таблицы указателей на виртуальные функции. Так как динамический тип указателя Child *, то компилятор найдет указатель на виртуальную функцию в объекте типа Child.

person Vlad from Moscow    schedule 10.12.2019
comment
как насчет obj = new Child() и obj2 = new Child(); , разве это не изменение obj и превращение его в дочерний объект?... и что вы имеете в виду под статическим типом? , я нигде не использую ключевое слово static - person J.C.VegaO; 11.12.2019
comment
@juancarlosvegaoliver obj и obj2 являются указателями. Они не являются объектами класса. Их статический тип — Отец. Таким образом, объявления вызываемых функций-членов будут искать в родительском классе. - person Vlad from Moscow; 11.12.2019
comment
@juancarlosvegaoliver Да, оба указателя указывают на объекты типа Child. То есть динамический тип указателей — Child *. Именно из-за динамического типа указателей будет вызываться виртуальная функция из класса Child. - person Vlad from Moscow; 11.12.2019
comment
Статический тип - неизменный во время выполнения. Father * — это Father * период. Он не изменится с указателя на Father. Тип, на который указывает указатель, является динамическим. Это может быть что угодно, производное от Father, и это не будет известно до времени выполнения. - person user4581301; 11.12.2019
comment
@ Влад из Москвы Я добавил фрагмент кода, чтобы узнать, становится ли obj (типа Отец*) потомком Child* после new(см. код выше), и в результате он остается объектом Отца. Так что это неправда, что оба указатели указывают на объекты типа Child. - person J.C.VegaO; 11.12.2019
comment
@juancarlosvegaoliver В этих утверждениях Отец fa fa=*obj; вы получили объект типа Отец. Вы объявили объект типа Отец. Так что вы ожидаете?. - person Vlad from Moscow; 11.12.2019
comment
@ Влад из Москвы Я ожидал, что obj = new Child() сделает obj указателем на Child, как вы сказали, так что в fa=*obj fa получает значение Child, на которое указывает obj, преобразуя fa в Child - person J.C.VegaO; 11.12.2019
comment
@juancarlosvegaoliver Подобъект типа Отец объекта типа Ребенок присваивается объекту типа Отец. Называется нарезка. - person Vlad from Moscow; 11.12.2019
comment
Не существует такой вещи, как динамический тип указателя. (Также неясно, существует ли динамический тип чего бы то ни было. Но это другой вопрос.) - person curiousguy; 13.12.2019