CodeInside
@CodeInside

Как использовать «гибкость» двумерного массива, перед одномерным?

class Student
{
	char * name;
	char * surname;
	int age;
	char phone[12];
	double average;
public:
	// methods
};

class AcademyGroup
{
	Student ** pSt;
	int count;
public:
	// methods
};


Задание заключается в реализации методов класса AcademyGroup, атрибутом которого является ДВУМЕРНЫЙ динамический массив. Преподаватель сказал, что таким образом данные станут более гибкими. Например, если в группе 1000 студентов, а нужно добавить ещё 1-го, то придётся создавать новый блок в 1000 ячеек, перезаписывать в него данные, переобъявлять старый на 1001 ячейку и обратно данные. А если массив двумерный, то можно избежать этой суматохи. Но я не могу понять как, ибо пропустил эту тему (но динамическое выделение памяти отлично знаю: по процедурном программировании проходили). Можете пожалуйста объяснить каким образом можно использовать эту "гибкость" на каком-либо примере?
  • Вопрос задан
  • 304 просмотра
Решения вопроса 1
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Здесь скорее всё-таки не двумерный массив, а одномерный массив содержащий указатели на экземпляры класса Student. Соответственно, при добавлении студента копируются не сами экземпляры, а только ссылки на них.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
@Mercury13
Программист на «си с крестами» и не только
Итак, перед нами задача: сделать динамический массив «умных указателей единоличного владения». Умный указатель единоличного владения (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>.
Ответ написан
В вашей структуре данных не двумерный массив используется, а массив указателей.
Массив указателей вам всё равно придётся двигать при добавлении\удалении элементов. Но это эффективнее, чем двигать массив объектов.
Ответ написан
Комментировать
@abcd0x00
Тут тебе набросал пример разделения большого массива на подмассивы.
Код
#include <iostream>

using namespace std;

class Student {
    char *name;
    char *surname;
    int age;
    char phone[12];
    double average;
public:
    Student() {};
    ~Student() {};
    // methods
    void set_age(int n) {
        age = n;
    }
    void print_age() {
        cout << age << endl;
    }
};

class AcademyGroup {
    enum { LINES = 10, ONE_LINE = 50 };
    Student ***pSt;
    int count;
public:
    AcademyGroup();
    ~AcademyGroup();
    // methods
    void add(Student *p);
    void print();
};

AcademyGroup::AcademyGroup()
{
    pSt = new Student **[LINES];
    for (int i = 0; i < LINES; i++)
        pSt[i] = new Student *[ONE_LINE];
    count = 0;
}

AcademyGroup::~AcademyGroup()
{
    for (int i = 0; i < LINES; i++)
        delete [] pSt[i];
    delete [] pSt;
    count = 0;
}

void AcademyGroup::add(Student *p)
{
    pSt[count / ONE_LINE][count % ONE_LINE] = p;
    count++;
}

void AcademyGroup::print()
{
    for (int i = 0; i < count; i++) {
        pSt[i / ONE_LINE][i % ONE_LINE]->print_age();
    }
}

int main()
{
    AcademyGroup group;

    for (int i = 0; i < 200; i++) {
        Student *p = new Student;
        p->set_age(i + 1);
        group.add(p);
    }
    group.print();
    return 0;
}


Вывод
[guest@localhost cpp]$ .iso++ t.cpp -o t
[guest@localhost cpp]$ ./t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
[guest@localhost cpp]$


По поводу ёмкости: вот эту часть, отвечающую за двумерность, можно перевыделять. То есть изначально, например, было 10 строк по 50 элементов, а потом на 501 элементе ты просто берёшь эти 10 строк перевыделяешь с сохранением того, что там было, и продолжаешь в том же духе. Перевыделение у тебя должно быть в функции добавления элемента.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы