Запутался в виртуальных функциях C ++ - PullRequest
2 голосов
/ 30 июля 2011

Я c ++ n00b, и я не уверен, что посмотрел в нужных местах, но я запутался в этом:

include <iostream>

using namespace std;

class Enemy
{
    public:
        void sayHere()
        {
            cout<<"Here"<<endl;
        }
        virtual void attack()
        {
        }
};

class Monster: public Enemy
{

    public:
        virtual void attack()
        {
            cout<<"RAWR"<<endl;
        }

};

class Ninja: public Enemy
{

    public:
        virtual void attack()
        {

            cout<<"Hiya!"<<endl;
        }
};

int main()
{
    Ninja n;
    Monster m;
    Enemy enemies[2];
    enemies[0] = m;
    enemies[1] = n;

    for(int i = 0; i<2; i++)
    {
        enemies[i].sayHere();
        enemies[i].attack();//Will not be overriden
    }
    return 0;
}

Мой вопрос: почему не атака () функция в классе монстров или ниндзя переопределяется?Любая помощь, даже ссылка, будет принята с благодарностью.

Ответы [ 5 ]

5 голосов
/ 30 июля 2011

Просто сделай:

Enemy* n = new Ninja();
Enemy* m = new Monster();

n->sayHere();
n->attack();
m->sayHere();
m->attack();

delete n;
delete m;

Это должно делать, как вы хотите. Вам нужно использовать указатели, чтобы он работал. Причина заключается в том, как работает динамическое связывание.

Всякий раз, когда в программе объявлена ​​виртуальная функция C ++, для класса создается v-таблица. V-таблица состоит из адресов виртуальных функций для классов и указателей на функции из каждого из объектов производного класса. Всякий раз, когда происходит вызов функции для виртуальной функции c ++, v-таблица используется для преобразования в адрес функции. Вот как происходит динамическое связывание во время вызова виртуальной функции.

Идея состоит в том, что компилятор сохраняет указатели на каждый метод на основе адреса памяти объекта. Требуется указатель для доступа к таблице и вызова соответствующего указателя функции. Подумайте, если бы вы хотели написать OO-версию C, как бы вы предоставили такой механизм, как наследование и полиморфизм? Это имеет смысл, когда вы думаете об этом таким образом.

Я только что прочитал, что вы уходите с JAVA. В JAVA большинство ваших объектов хранятся в куче. Это все просто подразумевается.

JAVA в

Enemy n = new Ninja();
n.attack();

примерно эквивалентно

Enemy* n = new Ninja();
n->attack();

Где. Оператор в JAVA больше похож на оператор -> в C ++. В обоих случаях n находится в куче. Java просто скрывает от вас все средства управления указателями и памятью. Вот почему у вас может быть блаженное незнание того, как динамическое связывание работает в JAVA и C #.

4 голосов
/ 30 июля 2011

Это связано с тем, что вы не получаете доступ к своим врагам через указатели:

    Ninja n;
    Monster m;

    Enemy *enemies[2];

    enemies[0] = &m;
    enemies[1] = &n;

    for (int i = 0; i < 2; i++)
    {
        enemies[i]->sayHere();
        enemies[i]->attack();
    }
    return 0;
2 голосов
/ 30 июля 2011

Если вы не обращаетесь к объекту через указатель или ссылку, вызовы виртуальных функций не будут работать.Чтобы заставить его работать, вам нужно переписать массив врагов как

Enemy *enemies[2];
enemies[0] = &m;
enemies[1] = &n;

Обратите внимание, что вам придется изменить все enemies[i]. на enemies[i]->.

1 голос
/ 30 июля 2011
Enemy enemies[2];   
enemies[0] = m;    
enemies[1] = n;

Это просто нарезка объекта - будет скопирован только объект Enemy из производных объектов.Виртуальные функции не могут играть роль.

0 голосов
/ 30 июля 2011

Это явление называется нарезка объектов .

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