Не понимаю назначение virtual

На протяжении всего изучения языка никак не могу понять и усвоить назначение ключевого слова virtual

Заранее извиняюсь за сей бред что возможно мог сморозить,если лень читать то сразу посмотрите на предпоследнее предложение)

Из всяких форумов я вырыл и запомнил следующее, о этом волшебном слове:

  1. Когда что-то принимает различные формы,но тем не менее продолжает свою суть. Попробую объяснить просто. Думаю имеется ввиду что у меня есть какая-то там функция Draw класса Base рисующая круг,тут я сделал её виртуальной,создал новый класс Base2 с такой же функцией Draw,наследовал базовый класс с кругом и после типо могу вызывать Base b1=new Base(); ну и Base2 b2=new Base(); а дальше вызывать нужное рисование. Но уже тут,я не понимаю смысла. Окей,я видел рассуждения на каком-то форуме что-то типо:

Зачем это вам нужно? Нужно что бы постоянно не делать приведение к каждому классу(пытался на память повторить код,но лучше не буду,а то будет просто смех какой-то)

Но ведь разве мне сложно делать приведение каждого? Да нет вроде,вызывать топорно тоже не сложно.

  1. Одновременно является носителем многих форм. Дальше был пример типо,у нас есть алмаз и есть графит,оба из углерода. Типо углерод принимает разные формы,он у нас тут виртуален. Окей,ну тут +- понятно,что в таком случае поставить виртуальность(поставить слово которое ставят все без объяснения)

Опять же,пункт описания для определения нужно ли ставить ключевое слово. Но зачем? не понятно.

Я не знаю что и написать.Из написанного мной же выше,я не могу прийти к выводу зачем мне использовать virtual и что это значит. Везде его вижу,аж стыдна,что я один такой не понимаю.

Помогите разобраться в этом слове, пожалуйста Х) я даже не столько синтаксис не понимаю,сколько смысл и преимущества в использовании

P.s. только пожалуйста,пишите простыми ответами,а то часто ответ вроде есть,но что бы его понять нужно загуглить и просидеть пол дня.


Ответы (1 шт):

Автор решения: Roman Konoval

Рассмотрим пример.

Некий разработчик создает библиотеку, с помощью которой, можно написать свою игру. Идея в том, чтобы можно было задать определения своих игровых объектов, их поведение, то как они выглядят в виде классов, слинковать с библиотекой, запустить код типа:

Game game = new Game();

game.addGameObject(new Hero());
game.addGameObject(new Enemy1());
game.addGameObject(new Enemy2());

game.run();

Game внутри себя хранит вектор игровых объектов, а run (среди прочего) вызывает код для рисования всех игровых объектов типа такого:

void Game::drawAllGameObjects() {
   ...
   for (vector<GameObject*>::iterator it = gameObjects.begin(); it != 
gameObjects.end(); it++) {
      (*it)->draw(canvas);
   }
}

Тут классы Hero и Enemy1 - создаются разработчиком игры, про них разработчик библиотеки ничего не знает. Он написал метод run и класс GameObject предполагая, что разработчик игры для каждого игрового объекта создаст класс-наследник от GameObject и напишет реализацию draw. Она будет рисовать используя примитивы типа drawPixel, drawLine и copyBitmap игровой объект.

Из библиотеки:

struct GameObject {
   virtual void draw(Canvas* canvas) {
   }
};

Из программы:

struct Hero : GameObject {
   void draw(Canvas* canvas) override { // тут ключевое слово virtual необязательно
      ...
   }
};

При этом реализацию библиотеки, в частности метод drawAllObjects разработчик библиотеки скомпилирует заранее и будет распостранять в виде бинарного файла (т.е. без исходного кода).

Для того, чтобы в функции drawAllObjects был вызван нужный метод (т.е. для Hero метод Hero::draw, а для Enemy1 - Enemy1::draw), о которых разработчик библиотеки ничего не знает, и нужно объявить метод GameObject::draw как virtual. В этом случае компилятор создаст правильный код для вызова метода из конкретного объекта, который будет доступен только во время выполнения (грубо - это будет косвенный вызов метода, т.е. адрес метода не прошит в коде, а берется из таблицы, которая хранится в конкретном объекте).

Если нет virtual, то в месте вызова draw компилятором будет вставлен вызов метода GameObject::draw не зависимо от типа объекта в it и эта задумка с drawAllGameObject не будет работать.

→ Ссылка