Member 13913102 Ответов: 2

В чем причина ошибки компиляции здесь?


Прошу экспертов развеять мои сомнения.
Я не понимаю, почему он выдает ошибку компиляции. Это полиморфные классы, в main я назначил базовый указатель с указателем производного класса, он должен найти func_2, так как __vptr базы будет указывать на __vtable производного класса.
Или у меня есть некоторый пробел в моем понимании __vtable и __vptr.
Пожалуйста, предложите мне несколько хороших статей, чтобы прояснить мои нижеприведенные сомнения.
class base
{
  public:
     virtual int func_1()
       {
         cout << "In func_1" ;
       }
};
class deri: public base
{
    public:
        virtual int func_2()
        {
          cout << "in func_2"; 
        }
};

main()
{
 base *ptr = new deri;
 ptr->func_2();//ptr is pointing to derived object
}


Что я уже пробовал:

Я попробовал его скомпилировать, он выдает ошибку компиляции:
main.cpp: In function ‘int main()’:
main.cpp:32:6: error: ‘class base’ has no member named ‘func_2’
 ptr->func_2();
      ^

Member 13913102

vtable объяснение, которое я узнал, находится здесь:
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/

2 Ответов

Рейтинг:
20

Afzaal Ahmad Zeeshan

О боже, у тебя там все перепутано с полиморфизмом и безопасностью типов. То, что вы ожидаете, происходит неправильно, это не то, почему тип наследуется. Наследование работает от родителя к ребенку. Таким образом, функция в родителе гарантирована—давайте на минуту проигнорируем модификаторы доступа в наследовании—в детском типе, но не наоборот. Вы не можете вызвать дочернюю функцию из родительского типа. Родительский тип просто не знает, что это за функция. Так,

class Foo { 
   int a;
   int b;
};

class Bar {
   string name;
};
В приведенном выше коде вы можете сделать это,
Bar b;
cout << b.a; // a comes from Foo
Но вы не можете этого сделать,
Foo f;
cout << f.name; // name does not exist
Но в наследовании полиморфизм работает просто отлично. Итак, в вашем коде, если мы сделаем это,
class deri : public base
{
public:
    int func_1() override  // <-- let's override, since base has it virtual
    {
        cout << "Derived func_1";
        return 0; // <-- Why did you forget this and didn't complain? 
    }

Теперь, когда вы запускаете тот же код,
base* base = new deri;
base->func_1();
Он будет печатать: "производная функция func_1". Почему? Из-за полиморфизма.

Я не хочу посвящать вас в детали модели памяти C++, потому что все эти детали применяются там. Просто помните, что наследование работает от родителя к потомку, но полиморфизм заставляет вызовы функций выполняться на фактическом типе экземпляра—в вашем случае new deri;.

Пожалуйста, смотрите ссылки ниже, чтобы узнать немного больше об этой концепции,
Полиморфизм (информатика) - Википедия[^]
Тип безопасности - Википедия[^]
Модель памяти - cppreference.com[^]
c++ - как на самом деле работает наследование? - переполнение стека[^]
Дружба и наследование - учебники по C++ [^]


Member 13913102

thx для вашего подробного объяснения, я снова пройдусь по вашему ответу, у меня все еще есть несколько сомнений.
Если в базовом классе я переименую func_1 в func_2, то это будет работать нормально, и функция производного класса будет вызвана, как это делают концепции __vtable и __vptr. __vptr будет иметь адрес __vtable производного класса.
Но прежде чем переименовать func_1 в func_2(в исходном коде), почему __vptr не смог найти виртуальный func_2

vtable объяснение, которое я узнал, находится здесь:
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/

Afzaal Ahmad Zeeshan

Если в базовом классе я переименую func_1 в func_2, то это будет работать нормально и будет вызвана функция производного класса

Нет, но потому, что теперь базовый тип будет иметь func_2, а не func_1. В этом случае, если вы попытаетесь вызвать func_1 на базе, он выдаст ту же ошибку.

Я не совсем понимаю, что вы подразумеваете под последним предложением в первом абзаце. Виртуальная таблица создается для типа, который создается, как и в вашем случае, это был производный тип. Таким образом, виртуальная таблица была создана для типа deri и заставила его знать функции. Но когда вы пытаетесь вызвать функцию func_2, базовый тип не имеет никакой информации об этом.

Помните, что виртуальная таблица существует во время выполнения. Эта ошибка возникает во время компиляции, поэтому концепция виртуальной таблицы еще не существует. При построении и выполнении программы ваша виртуальная таблица берет управление на себя, а модель памяти C++ заставляет программу принимать полиморфизм.

Member 13913102

да, я согласен с вами. Это означает, что первый компилятор должен согласиться с этим, vtable появляется на картинке во время выполнения.
Thx за ваши ценные комментарии и ответы.

Afzaal Ahmad Zeeshan

Да, вы правы.

Пожалуйста, также попробуйте отметить ответы как решение, если они помогли вам.

Рейтинг:
0

CPallini

Чтобы заставить полиморфизм работать, имя метода (и сигнатура) должны быть одинаковыми. Бытие ptr действительность deri указатель, вы все еще можете получить доступ к его func_2 метод через downcast, но это не полиморфизм:

#include <iostream>
using namespace std;

class base
{
  public:
     virtual void func_1()
       {
         cout << "In func_1 of base.\n" ;
       }
};
class deri: public base
{
    public:
        virtual void func_1() override
        {
          cout << "in func_1 of deri.\n";
        }
        virtual void func_2()
        {
          cout << "in func_2 of deri.\n";
        }

};

int main()
{
 base *ptr = new deri;
 ptr->func_1();//ptr is pointing to derived object
 (dynamic_cast<deri *>(ptr))->func_2(); // this is not polymorphism, though.
}


Member 13913102

да, я пропустил полиморфизм, но смотрите эту ссылку, она говорит, что __vptr будет указывать на __vtable производного класса, и эта __vtable будет иметь указатель на func_2, тогда он должен быть доступен.
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/

CPallini

На самом деле он доступен, как показывает мой код.