Наследование и работа с памятью в STL

Мы рассмотрели хранение данных в виде объектов внутри STL-ных контейнеров. Однако часто данные хранятся там не по значению, а по адресу. Как вы знаете из курса ассемблера – адрес это Int. STL-ные типы не умеют освобождать автоматически память при удалении адресов таких типов, по вполне очевидным причинам:

1) vector<int*>: вектор не знает, что элемент int* это единичный элемент в динамической памяти или массив таких объектов.

2) Когда объект создается вовне, то и код удаления, очевидно, должен где-то снаружи контейнерного типа находится.

Давайте рассмотрим пример: мы делаем какую-то игру, в которой часть объектов, допустим, может двигаться, а часть – нет.

#include "stdafx.h"

#include <iostream>

#include <exception>

#include <list>

#include <conio.h>

 

using namespace std;

 

class Drawable

{

public:

virtual void Draw() = 0;

virtual ~Drawable() {};

};

 

class Movable

{

public:

virtual void Update(int deltaTMsec) = 0;

virtual ~Movable() {};

};

 

class Fish : public Drawable, public Movable

{

private:

int m_x;

int m_y;

static const int Speed = 600;

public:

Fish(int x, int y) : m_x(x), m_y(y)

{

}

 

virtual void Update(int deltaTMsec)

{

cout << "Moving fish!\n";

m_x += deltaTMsec*Speed;

m_y += deltaTMsec*Speed;;

}

 

virtual void Draw()

{

cout << "Redrawing fish!\n";

}

};

 

class Hero : public Drawable, public Movable

{

private:

int m_x;

int m_y;

static const int Speed = 13;

public:

Hero(int x, int y) : m_x(x), m_y(y)

{

}

 

virtual void Update(int deltaTMsec)

{

cout << "Moving hero!\n";

m_x += deltaTMsec*Speed;

m_y += deltaTMsec*Speed;;

}

 

virtual void Draw()

{

cout << "Redrawing hero!\n";

}

};

 

class Wall : public Drawable

{

private:

int m_x;

int m_y;

public:

Wall(int x, int y) : m_x(x), m_y(y)

{

}

 

virtual void Draw()

{

cout << "Redrawing wall!\n";

}

};

 

int _tmain(int argc, _TCHAR* argv[])

{

auto fish = new Fish(13, 20);

auto mainHero = new Hero(13, 13);

auto wall = new Wall(33, 33);

 

list<Drawable*> drawableItems = { fish, mainHero, wall };

list<Movable*> movableItems = { fish, mainHero };

list<void*> allGameObjects = { fish, mainHero, wall };

 

// the main game circle

while (!_kbhit()) // not the end of the world!

{

for (Drawable* drawable : drawableItems)

drawable->Draw();

 

for (Movable* movable : movableItems)

movable->Update(13);

}

 

// clean memory

for (auto gameObjectIterator = allGameObjects.begin();

gameObjectIterator != allGameObjects.end();)

{

delete *gameObjectIterator;

gameObjectIterator = allGameObjects.erase(gameObjectIterator);

}

drawableItems.clear();

movableItems.clear();

 

return 0;

}

Здесь показано, как удалять объекты в динамической памяти. Поскольку выяснилось, что хочется завести несколько коллекций в зависимости от возможностей объектов: одна для того, чтобы перерисовывать, другая – двигать, в которой объекты могут повторятся, то соответственно решили завести еще одно общую коллекцию: в которой есть все объекты игрового мира: обратите внимание, как происходит удаление оттуда элементов. Остальные коллекции в дальнейшем просто очищаются.

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