Итак, перед нами задача: сделать динамический массив «умных указателей единоличного владения». Умный указатель единоличного владения (std::unique_ptr из C++11) — это указатель, который самолично владеет выделенной памятью; при исчезновении указателя исчезает и память.
Раз мы только учимся, мы не будем влезать в самописные шаблоны C++, готовые шаблоны STL (кроме algorithm и string) и новый, но уже реализованный во всех компиляторах стандарт C++11. Это довольно серьёзное ограничение; если его снять, можно серьёзно упростить себе жизнь. А для этого мы отделим структуру данных от жизненных объектов и реализуем объект StudentList. Пишу с листа, возможны ошибки.
Да, и без C++11 умный указатель единоличного владения реализовать довольно тяжело — поэтому структуру данных будем делать «монолитно», без разделения на умный указатель и динамический массив.
#include <algorithm>
class StudentList
{
public:
StudentList();
~StudentList();
Student& add(); // добавить пустого студента и выдать ссылку на новенького
size_t size() const { return fSize; }
Student& operator[](size_t i) { return *fData[i]; } // можно также наладить проверку диапазона — сделай это сам…
const Student& operator[](size_t i) const { return *fData[i]; }
void clear();
private:
typedef Student* PStudent;
PStudent* fData;
size_t fSize, fCapacity; // реальное кол-во студентов и на сколько студентов у нас заведено памяти.
// Указатели [fSize..fCapacity) резервные, их значение не определено и высвобождать
// их не надо.
enum { BLOCK_SIZE = 16; };
StudentList(const StudentList&) {} // копирование запрещаем, хочешь — реализуй сам и вынеси в public
StudentList& operator=(const StudentList&) { return *this; } // аналогично
};
StudentList::StudentList(); : fData(NULL), fSize(0), fCapacity(0) {}
Student& StudentList::add()
{
// Убедиться, что массива хватает; если нет — расширить
if (fSize >= fCapacity) {
size_t newCapacity = fCapacity + BLOCK_SIZE;
PStudent* newData = new PStudent[newCapacity];
std::copy(fData, fData + fSize, newData);
delete[] fData;
fData = newData;
fCapacity = newCapacity;
}
// Завести нового студента
Student* r = new Student;
fData[fSize++] = r;
return *r;
}
void StudentList::clear()
{
for (size_t i = 0; i < fSize; ++i)
delete fData[i];
delete[] fData;
fData = NULL;
fSize = 0;
fCapacity = 0;
}
StudentList::~StudentList()
{
clear();
}
Удаление и прочее сам наладишь?
А группа будет пользоваться нашим списком.
#include <string>
class AcademyGroup
{
public:
std::string name;
StudentList students; // при желании можно заинкапсулировать и его.
};
Это перед нами, правда, не двухмерный массив, как я уже сказал. Массив, хоть и Student**, но одномерный; каждый элемент массива — умный указатель единоличного владения. Если бы мы писали на STL C++11, это был бы
std::vector<std::unique_ptr<Student>>
.
Кроме УУЕВ, существует также умный указатель совместного владения std::shared_ptr.
Можно сделать второй вариант — массив из одномерных массивов. Если он заполнился — заводим ещё один массивчик. Пишется немного сложнее, особенно если не пользоваться STL. На STL —
std::deque<Student>
.