Наследование и работа с памятью в 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;
}
Здесь показано, как удалять объекты в динамической памяти. Поскольку выяснилось, что хочется завести несколько коллекций в зависимости от возможностей объектов: одна для того, чтобы перерисовывать, другая – двигать, в которой объекты могут повторятся, то соответственно решили завести еще одно общую коллекцию: в которой есть все объекты игрового мира: обратите внимание, как происходит удаление оттуда элементов. Остальные коллекции в дальнейшем просто очищаются.
Обратите внимание также на полиморфизм в обоих контейнерах: мы делаем контейнер адресов на базовый класс, а работаем с его наследниками, ожидая функционал, который есть пообещал базовый класс.