Классы и размещение объектов в heap-памяти
Под динамической памятью понимается в данном контексте heap-память. Для управления используются операторы new, delete. Правила их использования просты:
· если какая-либо переменная-указатель экземпляра инициализируется оператором new в конструкторе, то деструктор должен освободить память оператором delete, параметром которого является эта переменная указатель.
· все конструкторы должны базироваться для некоторого указателя либо на операторе new либо на new[], но ни в коем случае для одного и того же указателя нельзя их смешивать. Тогда и освобождение, соответственно, реализуется либо с помощью delete либо delete[].
Пример
Строится программная модель очереди.Будем считать, что экземплярами очереди являются структуры следующего вида:
struct Node
{
Item item;//данные о элементе
Struct Node* next;//указатель на следующий элемент.
};//Item – заранее определён
class Queue
{
enum {q_size = 10};
private:
Node* front;//указатель начала
Node* rear;//указатель конца
int items;//тек. кол-во элементов в очереди
const int qsize;//макс. кол-во элементов в оереди
public:
Queue(int sq = q_size);//конструктор по умолчанию
~Queue();//деструктор
bool isempty() const;//определяет пуста ли очередь
bool isfull() const;//оперделяет полна ли очередь
int queuecount() const;//кол-во элементов
bool enquene(const Item &item);//добавление элемента
bool dequeue(Item &item);//удаление элемента
};
Реализация класса
Мы не можем в конструкторе прямо указать qsize = sq, т.к. qsize – константа (const). Для обхода этой ситуации в Си используется специальный синтаксис – список инициализаторов, он включает перечень инициализаторов, разделённых запятыми. Списку, предшествует двоеточие:
Queue::Queue(int qs):qsize(sq)//инициализация qsize к qs
{
front = rear = NULL;
items = 0;
}
Только конструкторы могут применять список инициализаторов. Можно переписать конструктор и так:
Queue::Queue(int qs):qsize(sq),front(NULL),rear(NULL),items(0){}
Статичные элементы не могут инициализироваться списком инициализаторов!
Реализация методов:
bool Queue::enquene(const Item &item)
{
if (isfull()) return false;//очередь полна
Node* add = new Node;//созд узла
if (add == NULL) return false;//переполнение хипа
add->item = item;//запись данных
add->next = NULL;
items++;
if (front==NULL)//занесение в список очереди
front = add;
else rear->next = add;
read = add;
return true;
}
bool Queue::dequeue(Item &item)
{
if (front == NULL) return false;//очередь пуста
item = front ->item;//выборка из очереди
items--;
Node* temp = front;
front = front->next;
delete temp;//удаление свзующего элемента
if (items ==0) rear = NULL;
return true;
}
Необходим явный деструктор, чтобы при уничтожении всего объекта очереди удалить предварительно из неё все узлы:
Queue::~Queue()
{
Node* temp;
while (front!=NULL)
{
temp = front;
front = front -> next;
delete temp;
}
}
Желательно доопределить для полноты конструктор копирования и перегрузить оператор присваивания. Это делается добавлением следующих определений в раздел private декларации класса:
1. Queue(const Queue& q):qsize(q.qsize){…}; В теле «...» - надо пройти в цикле по списку всех элементов в q, разместить их копии в heap- памяти, связать в список и, соответственно, заполнить front, rear и items для нового объекта.
2. Queue& operator=(const Queue &q) {… return *this;} – Здесь практически проделать то же самое, но контролируя возможность того, что qsize < q.qsize
Поэлементное копирование, реализуемое обычным оператором «=» не годится, поэтому мы его перекрыли. Не годится и стандартное определение конструктора копирования, поэтому все определения мы упрятали в приватную часть декларации.
Поэлементное копирование в нашем случае не корректно, так как одна из очередей может добавлять элементы или удалять, а вторая ничего об этом не будет знать, и наоборот. Нарушается вся логика работы с независимыми списками.
Дата добавления: 2016-05-26; просмотров: 1523;