2015-12-19 3 views
1

В настоящее время я работаю над примерами из «C++ через Game Programming», и я пришел к этому примеру, что доказывает полиморфизмИспользование базового класса указателей на производный класс объектов

#include <iostream> 

using namespace std; 

class Enemy 
{ 
public: 
    Enemy(int damage = 10); 
    virtual ~Enemy(); 
    void virtual Attack() const; 

protected: 
    int* m_pDamage; 
}; 

Enemy::Enemy(int damage) 
{ 
    m_pDamage = new int(damage); 
} 

Enemy::~Enemy()    
{ 
    cout << "In Enemy destructor, deleting m_pDamage.\n"; 
    delete m_pDamage; 
    m_pDamage = 0; 
} 

void Enemy::Attack() const 
{ 
    cout << "An enemy attacks and inflicts " << *m_pDamage << " damage points."; 
} 

class Boss : public Enemy 
{ 
public: 
    Boss(int multiplier = 3); 
    virtual ~Boss(); 
    void virtual Attack() const; 

protected: 
    int* m_pMultiplier; 
}; 

Boss::Boss(int multiplier) 
{ 
    m_pMultiplier = new int(multiplier); 
} 

Boss::~Boss()     
{ 
    cout << "In Boss destructor, deleting m_pMultiplier.\n"; 
    delete m_pMultiplier; 
    m_pMultiplier = 0; 
} 

void Boss::Attack() const 
{ 
    cout << "A boss attacks and inflicts " << (*m_pDamage) * (*m_pMultiplier) 
     << " damage points."; 
} 

int main() 
{ 
    cout << "Calling Attack() on Boss object through pointer to Enemy:\n"; 
    Enemy* pBadGuy = new Boss(); 
    pBadGuy->Attack(); 

    cout << "\n\nDeleting pointer to Enemy:\n"; 
    delete pBadGuy; 
    pBadGuy = 0; 

    return 0; 
} 

Мой вопрос, почему эта линия была используется:

Enemy* pBadGuy = new Boss() 

вместо

Boss badGuy; 
badGuy.Attack(); 

?

Автор называет это «Использование указателей класса« Малыши »для производных объектов класса». Часто ли он используется? Имеет ли он какие-либо преимущества в отношении «нормального» метода создания экземпляров?

Спасибо!

+0

Возможный дубликат [Почему я должен использовать указатель, а не сам объект?] (Http://stackoverflow.com/questions/22146094/why-should-i-use-a-pointer-rather- чем сам объект) –

+0

Сделайте себе одолжение и найдите другую книгу, чтобы изучить программирование на С ++. В этом примере кода есть много ошибок, которые вам просто нужно будет отучить. Подобно 'using namespace std;', те 'int *' ', которые указывают на один 'int' (возможно, только что использовали' int'), присваивая '0' указателю непосредственно перед его удалением (пустая трата времени); все эти бесцельно защищенные члены. –

+0

Я вижу вашу мысль, и я читаю и другие книги. Я действительно не понимаю, почему присвоение 0 указателю. Я думаю, что эта книга объясняет некоторые понятия (например, классы, векторы, указатели и ссылки) лучше других. Я тоже читал Прату, но он настолько детализирован во многих местах, и вроде бы теряет фокус от основного предмета. – Sabyc90

ответ

2

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

Предлагаемая вами альтернатива наглядно не демонстрирует эту ключевую концепцию.

Обе альтернативы выполняют одно и то же, но это предназначено для примера отправки виртуального метода.

+0

Спасибо! Это очень ясно! – Sabyc90

0

Подумайте о следующем: у вас есть молотки и пилы. Это оба инструмента. Они находятся в мешках (например, в векторах), и если вы хотите их использовать, вы вытаскиваете один из сумки, а затем используете его (назовем метод объекта). Вы не можете ставить молотки в мешок пилы и наоборот, но вы можете положить их в мешок под названием инструменты. Не проще иметь один мешок вместо двух. Если вы хотите их использовать, вы используете его соответствующим образом (вызовите виртуальный метод). Таким образом, ваши враги могут быть легко обработаны с помощью полиморфизма, а затем без него. Кроме того, вы можете создать методы, такие как

// in your class... 
virtual void attackedByOther(const Enemy& other); 

В этом случае вам нужно только одна функция реализуется несколько раз, что делает расчет ущерба и т.д.

Так, отвечая на ваш вопрос: если вы используете указатели вы можете делай полиморфизм, но с инстансом ты не можешь.

Надеюсь, вы понимаете!

0

Просто, чтобы добавить немного к тому, что другие люди говорят ...

Одной из основных причин использовать абстрактные классы и виртуалов, так что вы можете иметь несколько типов объектов в одном массиве.

Вот пример из проекта я сделал в школе:

  char buffer = '\0'; 
      int itemindex = 0; 
      Item* myitem;//.................................. I used a polymorphic pointer Item 

      while (!myfile.get(buffer).fail()){//............ Check for failure each loop iteration 

       if (buffer == 'N' || buffer == 'P') { 

        if (_noOfItems - 1 >= itemindex) {//.... -1 because of index 0 and >= to account for the first set of entries 
         delete _items[itemindex];//.......... if it is >= than there has already been allocation at that index, so it must be freed 

        }//...................................... to avoid memory leak 
        if (buffer == 'P') { 
         myitem = new Perishable();//......... polymorphic pointer static type is now perishable (dynamic will always be item) 

        } else if (buffer == 'N') {//............... else is extra safe 
         myitem = new NonPerishable();//....... polymorphic pointer static type is now nonPerishable (dynamic is item) 

        } 

        myfile.ignore();//....................... ignore comma 
        myitem->load(myfile); 

        _items[itemindex] = myitem;//............ This line assigns myitem to the item index, since its polymorphic, only have to write 
               //............. it once, within the 'N' || 'P' scope. 
        itemindex++;//........................... This must incriment every time 'N' || 'P' is encountered, cause each represents an 
       }//.......................................... item entry. 

      } 

То, что мы должны были сделать, это создать Скоропортящиеся и стойкие пункты. Если вы будете следовать комментариям, вы увидите, что я создал указатель Item (базовый класс), а затем на основе файла, который читается, если char является «P», я создаю объект скоропортящихся объектов (производный) или NonPerishable (также полученный) ,

Итак, суть здесь в том, что _items[itemindex] = myitem; вызывается только один раз, а не в каждом состоянии отрасли buffer = P/N

Там же несколько интересных вещей происходит в этом примере, но, как я уже говорил, как скоропортящиеся (ребенок) и NonPerishable (child) находятся в массиве Item (parent).

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

for(int i = 0; i < entityList.length; i++){ 
    entityList[i].attack 
} 

Теперь это/действительно/круто. Вы можете указать всем сущностям сделать одно и то же в одной строке, потому что они имеют один и тот же родительский тип.

Что вам действительно нужно знать, это то, о чем уже говорилось о динамической отправке. Способ, который мне был объяснен, прост: объект может иметь динамический тип и статический тип.

Динамический тип тип ссылки был создан, например:
Item* myitem //Dynamic type is Item

Статический тип является то, что указатель/в настоящее время/указывает. Так что теперь статический тип myitem также является типом Item.

Если я это сделать:

myitem = new NonPerishable();

указатель myitem теперь указывает на тип ребенка 'стойкому'. Динамический тип не изменяется, поскольку он был создан как Item. Таким образом, динамический тип есть/still/type Item. Статический тип, однако, теперь NonPerishable, потому что указатель Item (myitem) теперь указывает на объект NonPerishable.

Примечание: Dynamic является то, что она была создана, как (это может быть контрпродуктивным интуитивное именем, но это так)

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

Раннее связывание означает, что функция, которая срабатывает, будет родительской функцией, динамическая отправка означает, что функция, которая срабатывает, будет дочерней. Раннее связывание по умолчанию для C++, для получения динамической отправки вы должны объявить эту функцию как «виртуальную», тогда по умолчанию будет дочерняя функция.

Одним из способов переопределения привязки является явное объявление пространства имен. Вот пример:

class Parent; //pseudo code 
class Child : public Parent 

object.Child::myfunction() 

Примечание: Обычно, если родитель и ребенок имеют ту же функцию (MyFunction) будет раннее связывание (родительская версия). В этом случае вы используете пространство имен child ::, чтобы оно вызывало дочернюю версию myfunction.

Это много информации, если у вас есть какие-либо вопросы, которые я могу разработать.

0

Я просто расскажу о том, что сказал @SamVarshavchik, и отвечайте на ваши вопросы последовательным образом.

  1. Эта конкретная декларация была использована, так как это помогает нам использовать концепции динамического связывания и использования виртуальных функций. Данные о каждом из них можно найти в Интернете. Короче говоря, динамическая привязка - это концепция, когда объект привязан к функции во время выполнения, а не во время компиляции, а виртуальные функции - это те, которые поддерживают динамическое связывание. Более того, динамическое связывание может выполняться только указателями и ссылками, а не с самими объектами, и поскольку указатель на базовый класс можно использовать в качестве указателя на производный класс, мы используем данное объявление.

  2. «Используется ли он часто?» Да, он используется очень часто, более того, всякий раз, когда задействован полиморфизм и концепция наследования, поскольку он не может каждый раз, чтобы заранее знать, для какого класса пользователь призван вызвать соответствующую функцию.

  3. Я думаю, что к настоящему моменту я уже ответил на ваш последний вопрос, поскольку я сказал, поскольку динамическое связывание может выполняться либо с указателями, либо ссылкой на базовый класс, а не с объектом, нам нужно использовать данный метод.

+0

Спасибо за этот ответ! Дело в том, что многие книги и онлайн-примеры говорят, что «ссылки могут также использоваться», но рядом с ними нет примеров использования ссылок вместо указателей. – Sabyc90

Смежные вопросы