Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.50/6: Рейтинг темы: голосов - 6, средняя оценка - 4.50
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
1

Ошибка при создании и заполнении динамического массива указателей на объекты

23.03.2022, 21:09. Показов 1129. Ответов 16

Author24 — интернет-сервис помощи студентам
Коллеги, пишу что-то типа простенького симулятора карточной игры с активным использованием указателей и new\delete конструкций (для тренировки).
Есть вот такой код:
Кликните здесь для просмотра всего текста

C++
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
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <process.h>
#include <conio.h>
 
using namespace std;
 
enum Fvalues {EMPTY=0,TWO=2, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE};
enum Suits   {NOSUIT=0, HEARTS=3, DIAMONDS, CLAWS, SPADES};
 
// CARD CLASS
class CARD 
{
private:
    Fvalues fval;
    Suits suit;
public:
    CARD(): fval(EMPTY),suit(NOSUIT) // no-arg c-tor
    {}
 
    CARD(Fvalues fv, Suits st): fval(fv), suit(st) // 2-arg c-tor
    {}
 
    CARD(int fv, int st): fval(static_cast<Fvalues>(fv)), suit(static_cast<Suits>(st))
    {}
 
    void display() const;
};
 
void CARD::display() const
{
    if (fval == 0 && suit == 0) { cout << static_cast<char>(254) <<" "<< static_cast<char>(254); goto End; } // если пустое значение ДОСТОИНСТВА и МАСТИ для вновь созданной карты выводим 2 квадратика!!!
 
    if (fval >= 2 && fval <= 9) cout << fval; // выводим достоинство карты
    else 
        switch(fval) 
        {
        case 10: { cout << "T"; break; }
        case 11: { cout << "J"; break; }
        case 12: { cout << "Q"; break; }
        case 13: { cout << "K"; break; }
        case 14: { cout << "A"; break; }
        default: {cout << "Unknown face value!"; }
        } // выводим достоинство карты
        
 
    if (suit >= 3 && suit <= 6) cout << static_cast<char>(suit); // выводим масть карты
    else cout << "Unknown suit!";
 
End: {}
 
}
// ......... CARD CLASS
 
 
//  DECK CLASS
class DECK
{
private:
    CARD* deck[52];
public:
    DECK()
    {
        int index = 0;
        for (int i = 3; i <= 6; i++)
        {
            for (int j = 2; j <= 14; j++) deck[index++] = new CARD(j, i);
        }
    }
 
 
    ~DECK() 
    {
        for (int i = 0; i < 52; i++) delete deck[i];
 
        //delete this;
    }
 
    void display() const;
    void swap(int ind1, int ind2);
    void shuffle();
    CARD* drawCrd(int i);
 
};
 
void DECK::display() const 
{
    for (int i = 0; i < 52; i++) 
    { 
        if (deck[i] == NULL) cout << "__";
        else
        {
            deck[i]->display();
        }
        cout << " ";
 
        if ((i % 13) == 12) cout << endl;
    }
}
 
 
void DECK::swap(int ind1, int ind2) 
{
    CARD* temp;
 
    temp = deck[ind1];
    deck[ind1] = deck[ind2];
    deck[ind2] = temp;
 
    //delete temp;
}
 
void DECK::shuffle() 
{
    int ind1, ind2;
    ind1 = ind2 = 0;
    srand(time(NULL)); // seed random numbers with time
    
    for (int j=0; j<=300; j++) 
    {
    INIT:
        ind1 = rand() % 52;
        ind2 = rand() % 52;
        if (ind1 == ind2) goto INIT;
        swap(ind1, ind2);
    }
}
 
CARD* DECK::drawCrd(int i)
{
    if (i < 0 || i > 51) { cout << "Out of DECK index!"; exit(1); }
 
    CARD* ptr = deck[i];
    deck[i] = NULL; // убираем указатель т.е. убираем карту из колоды!
 
    return ptr; // возвращаем указатель на карту под аргументом i
}
 
// ......... DECK CLASS
 
// SUBSET CLASS
class SUBSET 
{
private:
    CARD** subset;
    const unsigned int size;
public:
    SUBSET(): size(0), subset(NULL) 
    {}
 
    SUBSET(const unsigned int sz): size(sz)
    {
        subset = new CARD* [size];
        
    }
 
    ~SUBSET() 
    {
        for (int j = 0; j < size; j++) delete subset[j];
        //delete[] subset;
    }
 
    void fill(DECK deck) 
    {
        if (subset != NULL)
            for (int i = 0; i < size; i++) 
            {
                subset[i] = deck.drawCrd(i); 
            }
        
    
    }
    void display() const 
    {
        if (subset != NULL)
        for (int i = 0; i < size; i++) subset[i]->display();
    }
};
 
// ......... SUBSET CLASS
 
int main() 
{
    
    DECK d1;
    
    d1.display();
    
    d1.shuffle();
    cout << endl;
    d1.display();
 
    SUBSET sub1(6);
    sub1.fill(d1);
    
    return 0;
}


Суть в чём вкратце:
1. Есть класс CARD (объектов типа карта с полями достоинство и масть). Он умеет только создавать карту через различные к-торы и выводить на экран.
2. Есть класс DECK (колода из 52 карт), который представлен массивом из 52 указателей на объекты типа CARD.
Он умеет инициализировать объект Колоды с помощью new CARD() к-тора т.е. заполнять колоды картами по порядку.
Также умеет выводить всю колоду на экран, мешать 2 произвольные карты, а также перемешивать всю колоду. Это реализовано через перемешивание указателей. Ещё умеет вытаскивать одну карту из колоды по номеру, возвращая указатель на вытащенную карту и, соответственно, убирая её из колоды.
До этого момента всё работает, как часы!

3. Дальше сделал некий класс SUBSET (произвольное кол-во карт извлекаемых из объекта колоды класса DECK).
В нём сидит указатель типа CARD** т.е. pointer-to-pointer на начало массива, содержащего указатели на объекты типа CARD, а также размер выделяемого подмножества карт - size (size будет задан в к-торе объекта).
К-тор этого класса с числовым параметром инициализирует поле size, а затем динамически создаёт через конструкцию new Card*[size] массив указателей на некоторое подмножество карт (скажем 6). К-тор только выделяет память, но не заполняет его указателями на реальные карты!
Дальше функция данного класса SUBSET::fill(DECK), которая принимает на вход ранее созданный объект колоды,
должна заполнить созданный в к-торе SUBSET массив указателей на карты. Пока что беру для заполнения объекта подмножества просто первые карты подряд из перемешанной колоды т.е. без изысков. Всё вроде заполняется и потом выводится на экран с помощью метода SUBSET::dislplay(), ОДНАКО, уже после корректного отображения всех данных появляется ошибка:
"Debug Assertion Failed! (Expression:_CtrlsValidHeapPointer(Block))."
Экспериментальным путём с помощью _getch() после каждой "переброшенной" из колоды в подмножество карты установил, что ошибка возникает в методе SUBSET::fill() после внесения последнего э-та. Вроде размерности все соответствуют т.е. скажем выделяю место под массив указателей на 6 карт, дальше ровно 6 указателей из объекта колоды туда и отправляю, но ошибка не уходит...
Я явно, что то напутал либо с new, либо с delete, но и так и так пробовал и в к-торах и деструкторах и в fill() шаманил, никак не могу это отловить или как то исправить! Подскажите, пожалуйста, где у меня ошибка в коде и заодно соориентируйте правильно ли я использую delete в классах DECK и SUBSET для освобождения памяти?
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
23.03.2022, 21:09
Ответы с готовыми решениями:

Ошибка при заполнении динамического массива
#include &lt;iostream&gt; #include &lt;fstream&gt; using namespace std; int N, i, a, l; int head = -1;...

Ошибка при заполнении динамического массива
Добрый день! Для лабораторной работы нужно посчитать средневзвешенную высоту дерева оптимального...

Ошибка при заполнении динамического массива точек
Доброго времени суток. Пишу примитивный графический редактор. На данный момент застрял на...

Ошибка с функцией Val при заполнении двумерного динамического массива
Делал лабораторную работу. Задача создать таблицу, размер которой будет указываться пользователем....

Ошибка при компиляции двумерного динамического массива указателей
Нужно создать двумерный динамический массив указателей собственного типа Building в классе City, но...

16
 Аватар для igorrr37
2863 / 2011 / 988
Регистрация: 21.12.2010
Сообщений: 3,716
Записей в блоге: 15
24.03.2022, 10:39 2
указатели надо обнулять и после создания и после удаления
C++
1
subset = new CARD * [size] {nullptr};
C++
1
void fill(DECK& deck)
0
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
24.03.2022, 13:46  [ТС] 3
Цитата Сообщение от igorrr37 Посмотреть сообщение
1
subset = new CARD * [size] {nullptr};
Это поправил. Не знал, что их надо так "занулять" при выделении памяти, ибо мы же сразу в fill() в них копируем указатели из объекта deck т.е. они неопределёнными вроде не остаются.

Цитата Сообщение от igorrr37 Посмотреть сообщение
указатели надо обнулять и после создания и после удаления
А как можно обнулить указатель ПОСЛЕ его удаления?


Цитата Сообщение от igorrr37 Посмотреть сообщение
1 void fill(DECK& deck)
Это согласен, спасибо! Пропустил, что лучше объект в функцию передавать по ссылке!

К сожалению та же ошибка Debug Assertion по-прежнему появляется при завершении программы т.е. объект sub нормально заполняется картами из deck, и deck и sub корректно выводятся на экран, но УВЫ, то ли при выходе из main, то ли в процессе освобождения какой то памяти выскакивает этот неприятный error!

Добавлено через 14 минут
UPDATE1
Оказывается, я с момента задания вопроса в процессе отладки закомментил строку в определении метода CARD* DECK::drawCrd(int i)
C++
1
deck[i] = NULL; // убираем указатель т.е. убираем карту из колоды!
т.е. ту строку, где указатель на вытянутую карту обнулялся, поскольку она исключалась из колоды.

Так вот, когда строку раскоменчиваешь, ошибка исчезает, но если мы её оставляем не работающей
(т.е. указатели на карты уже скопировали в подмножество, но сами указатели на эти же карты исходной колоды оставляем без изменения, как будто колода не изменилась), этот баг опять начинает выскакивать.
Т.е. при исключении карт из колоды всё ОК и в колоде и в подмножестве. А при копировании в подмножество с оставлением в колоде Debug Assertion Failure. Логики вообще не понимаю.
0
 Аватар для igorrr37
2863 / 2011 / 988
Регистрация: 21.12.2010
Сообщений: 3,716
Записей в блоге: 15
24.03.2022, 13:59 4
Лучший ответ Сообщение было отмечено realalexandro как решение

Решение

Цитата Сообщение от realalexandro Посмотреть сообщение
А при копировании в подмножество с оставлением в колоде
два указателя на одну область памяти и два деструктора (дэк и сабсет) по очереди делают delete этих указателей
1
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
24.03.2022, 14:17  [ТС] 5
Цитата Сообщение от igorrr37 Посмотреть сообщение
два указателя на одну область памяти и два деструктора (дэк и сабсет) по очереди делают delete этих указателей
Ага! Так что нужно убрать один деструктор, например из сабсета, в случае, если решаем оставить "вытянутые" карты в колоде? Или как это исправить? Мне для понимания, поскольку путаюсь в теме освобождения памяти.

Добавлено через 12 минут
Ну да, мой косяк! Получается, поскольку мы в Deck и так в конце программы все указатели на карты удаляем через,
C++
1
2
3
4
5
6
~DECK() 
    {
        for (int i = 0; i < 52; i++) delete deck[i];
 
        //delete this;
    }
а в объекте subset после его заполнения в любом случае "сидит" часть указателей на те же объекты в памяти, поскольку мы их скопировали => второй раз удалять в дест-ре Subset уже нечего.
Правда непонятно почему, если мы ту часть указателей, которые из Deck присвоили элементам Subset предварительно в Deck обнулили, то тогда деструктор Subset, который работает после или одновременно с дестр. Deck эту ошибку уже не вызывает, ведь мы всё равно пытаемся второй раз сделать delete тех областей памяти, которые уже освободили первым деструктором Deck?
0
фрилансер
 Аватар для Алексей1153
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
24.03.2022, 15:40 6
realalexandro, std::unique_ptr<CARD> в помощь

Добавлено через 50 секунд
и std::vector
0
 Аватар для PencilTorch
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
24.03.2022, 15:57 7
Цитата Сообщение от realalexandro Посмотреть сообщение
А как можно обнулить указатель ПОСЛЕ его удаления?
Нюанс в том, что delete не удаляет указатель, а только освобождает память. Полезно после делета его занулять. К тому же это помогает потом в деструкторе делать проверку указателя на нуль перед делетом. Благодаря этому не возникает ситуации с попыткой двойного делета уже освобожденной памяти.
1
 Аватар для igorrr37
2863 / 2011 / 988
Регистрация: 21.12.2010
Сообщений: 3,716
Записей в блоге: 15
24.03.2022, 16:05 8
Цитата Сообщение от PencilTorch Посмотреть сообщение
делать проверку указателя на нуль перед делетом
это лишнее
and its value must be either null or pointer to a non-array object created by a new-expression
0
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
24.03.2022, 19:09  [ТС] 9
Цитата Сообщение от PencilTorch Посмотреть сообщение
Нюанс в том, что delete не удаляет указатель, а только освобождает память. Полезно после делета его занулять. К тому же это помогает потом в деструкторе делать проверку указателя на нуль перед делетом. Благодаря этому не возникает ситуации с попыткой двойного делета уже освобожденной памяти.
Так это где мне его надо обнулить получается после удаления - и в объекте DECK и в объекте SUBSET?
0
 Аватар для PencilTorch
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
25.03.2022, 13:46 10
realalexandro, drwCrd я бы написал вообще по другому. тут ведь надо и указатель очистить, и что-то вернуть. Уж лучше тогда ничего не возвращать. Хотя возможны варианты.
Потом эти goto, первый раз их увидел в коде
Не понял зачем в subset двойной указатель, но я вообще слабо в них ориентируюсь.

В общем, немного переписал код на свой вкус:
C++
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
201
202
203
204
205
206
207
#include <iostream>
#include <cstdlib>
#include <ctime>
 
using namespace std;
 
enum Fvalues { EMPTY = 0, TWO = 2, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE };
enum Suits { NOSUIT = 0, HEARTS = 3, DIAMONDS, CLAWS, SPADES };
 
// CARD CLASS
class CARD
{
private:
    Fvalues fval;
    Suits suit;
public:
    CARD() : fval(EMPTY), suit(NOSUIT) // no-arg c-tor
    {}
 
    CARD(Fvalues fv, Suits st) : fval(fv), suit(st) // 2-arg c-tor
    {}
 
    CARD(int fv, int st) : fval(static_cast<Fvalues>(fv)), suit(static_cast<Suits>(st))
    {}
 
    void display() const;
 
    void setFval(const int fv) {
        fval = static_cast<Fvalues>(fv);
    }
    void setSuit(const int st) {
        suit = static_cast<Suits>(st);
    }
    Fvalues getFval() const {
        return fval;
    }
    Suits getSuit() const {
        return suit;
    }
};
 
void CARD::display() const {
    if (fval == 0 && suit == 0) {
        cout << static_cast<char>(254) 
            << " " << static_cast<char>(254) << endl;
    } // если пустое значение ДОСТОИНСТВА и МАСТИ для вновь созданной карты выводим 2 квадратика!!!
 
    if (fval >= 2 && fval <= 9) 
        cout << fval; // выводим достоинство карты
    else
        switch (fval) {
        case 10: { 
            cout << "T";
            break;
        }
        case 11: {
            cout << "J";
            break;
        }
        case 12: {
            cout << "Q";
            break;
        }
        case 13: {
            cout << "K";
            break;
        }
        case 14: {
            cout << "A";
            break;
        }
        default: {
            cout << "Unknown face value!\n";
            break;
        }
    } // выводим достоинство карты
 
 
    if (suit >= 3 && suit <= 6)
        cout << static_cast<char>(suit) << endl; // выводим масть карты
    else 
        cout << "Unknown suit!\n";
}
 
//  DECK CLASS
class DECK
{
private:
    CARD* deck = nullptr;
    unsigned short SIZE;
public:
    DECK() {
        SIZE = 52;
        deck = new CARD[SIZE];
        initDesk();
    }
 
    ~DECK() {
        if (deck != nullptr) {
            delete[] deck;
            deck = nullptr;
        }
    }
    void initDesk() {
        int it = 0;
        for (int su = 3; su <= 6; su++) {
            for (int fv = 2; fv <= 14; fv++) {
                deck[it].setSuit(su);
                deck[it++].setFval(fv);
            }
        }
    }
    void display() const {
        for (int i = 0; i < SIZE; i++) {
            if (deck[i].getSuit() == 0 && deck[i].getFval() == 0)
                cout << "__\n";
            else {
                deck[i].display();
            }
            if ((i % 13) == 12) cout << endl; // ???
        }
    }
    void Swap(int ind1, int ind2) {
        CARD* temp = new CARD;
        temp->setSuit(deck[ind1].getSuit());
        temp->setFval(deck[ind1].getFval());
        deck[ind1].setSuit(deck[ind2].getSuit());
        deck[ind1].setFval(deck[ind2].getFval());
        deck[ind2].setSuit(temp->getSuit());
        deck[ind2].setFval(temp->getFval());
        delete temp;
    }
    void Shuffle() {
        int ind1, ind2;
        ind1 = ind2 = 0;
        srand(static_cast<unsigned int>(time(0))); // seed random numbers with time
        for (int j = 0; j <= 300; j++) {
            ind1 = rand() % SIZE;
            ind2 = rand() % SIZE;
            Swap(ind1, ind2);
            if (ind1 == ind2)
                j--;
        }
    }
    void drawCrd(CARD * ptr, int it) {
        if (it >= 0 && it < SIZE) {
            ptr[it].setSuit(deck[it].getSuit());
            ptr[it].setFval(deck[it].getFval());
            deck[it].setSuit(0);
            deck[it].setFval(0);
        }
        else
            cout << "Out of DECK index!\n";
    }
};
 
// SUBSET CLASS
class SUBSET {
private:
    CARD* subset = nullptr;
    unsigned short SIZE;
public:
    SUBSET() : SIZE(0), subset(nullptr) {}
 
    SUBSET(const unsigned short sz) : SIZE(sz) {
        subset = new CARD[SIZE];
    }
    ~SUBSET() {
        if (subset != nullptr) {
            delete[] subset;
            subset = nullptr;
        }
    }
 
    void fillSubset(DECK& deck) {
        for (int i = 0; i < SIZE; i++) {
            deck.drawCrd(subset, i);
        }
    }
 
    void display() const {
        if (subset != nullptr) {
            for (int i = 0; i < SIZE; i++) {
                subset[i].display();
            }
            cout << endl;
        }
    };
};
 
int main() {
    DECK d1;
    d1.display();
    cout << endl;
    d1.Shuffle();
    d1.display();
    cout << endl;
 
    SUBSET sub1(6);
    sub1.fillSubset(d1);
    sub1.display();
    cout << endl;
    d1.display();
    cout << endl;
 
    return 0;
}
Структура классов намекает на использование наследования, кстати.
1
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
25.03.2022, 15:03  [ТС] 11
PencilTorch, Спасибо за труд во-первых! Правда, "идеология" кода изменилась до неузнаваемости, но это, как говорится: "... как художник художнику..."
1. По поводу наследования. Класс SUBSET как раз задумывался как базовый, так что оно будет.
Двойной указатель Card** subset был нужен затем, что это указатель на массив УКАЗАТЕЛЕЙ, каждый из которых в свою очередь указывает на конкретную карту (А НЕ ПРОСТО УКАЗАТЕЛЬ НА МАССИВ ГОТОВЫХ КАРТ!) т.е. это примерно то же самое, что и массив указателей на карты CARD* deck[52] в классе DECK в моём коде.
Просто т.к. в колоде всегда 52 карты гораздо нагляднее так объявлять, что будет 52 указателя, чем просто объявлять указатель на массив карт CARD* deck = nullptr; и вводить переменную размерность unsigned short SIZE;, которая по сути не меняется. Я решил делать колоду именно указателями на карты, а не самими объектами карт, чтобы было быстрее их мешать (т.к. не понадобится копирование целых объектов при перестановках) т.е. в моей парадигме и в колоде и в SUBSET все действия должны происходить не с самими картами, которые раз и навсегда созданы в памяти(до срабатывания дест-ра), а с указателями на них т.е. с их адресами...
2. По поводу goto, его разумеется лучше вообще не использовать, но в данном случае т.к. это касается только локальной проверки того, что случайно генерируемые номера индексов массива CARD* deck[52] при перемешивании не должны совпадать, а если они случайно совпали, то сразу генерится новая пара, думаю, нормально.
3. CARD* DECK::drawCrd(int i) сначала копирует по индексу указатель во временную переменную, потом только зануляет соответствующий указатель в DECK и возвращает тот временный сохр. указатель т.е. мы получаем на выходе адрес карты, которую "вытащили" из колоды, он попадает в SUBSET, и одновременно из массива DECK мы его убираем т.к. одна и та же карта не может быть и на руках и оставаться в колоде, если это не шулер-казино
4. Проблему избежания "вторичного" удаления в дест-ре SUBSET тех объектов карт, которые уже могли быть удалены в рамках дестр-ра класса DECK (или наоборот), я решил путём добавления в каждую карту int поля count_ptrs в котором считается, сколько указателей в данный момент указывают на данную карту. Удаление в дест-ре каждой карты будет происходить только, когда остаётся только один указатель на данный конкретный объект. В противном случае просто уменьшаю счётчик числа указателей в карте в дестр-ре на 1 до тех пор пока от карты не "отвалятся" все указатели кроме одного. Когда же заводим карту через new и ставим на неё указатель счётчик указателей внутри карты ставим на 1 и потом увеличиваем на 1 каждый раз при копировании указателя на ту же карту в ещё одну переменную-указатель. При обнулении указателя уменьшаем соответственно. Для этого правда пришлось сделать классы DECK и SUBSET friend-ами по отношению к классу CARD. Хотя это нужно только на случай, если при вытаскивании карты из DECK в SUBSET (или из любого объекта некого класса в объект другого класса с указателями на карты) в колоде её оставляем т.е. не зануляем указатель и появляются множественные указатели на одну и ту же карту...

Я на самом деле чего хотел понять и чего так до конца и не понял! Вот у нас есть, допустим массив УКАЗАТЕЛЕЙ на карты (а не самих карт!) т.е. массив адресов в памяти, например CARD* DECK[52] или CARD* SUBSET[size] с произвольным размером в данном случае не важно.
Как НАДО подобный массив грамотно удалять в дест-ре:
а.
C++
1
2
   for (int i = 0; i < 52; i++) delete deck[i];
   deck=nullptr;
или достаточно сделать
б.
C++
1
2
   delete[] deck;
   deck=nullptr;
Просто в первом случае, насколько я понимаю и насколько я встречал в лит-ре, мы сначала удаляем сами ОБЪЕКТЫ карт из памяти, на которые указывал каждый эл-т массива указателей, а потом зануляем указатель на сам этот массив т.е. по сути уничтожаем и его. А во втором варианте у меня такое впечатление, что стираем просто массив адресов, а объекты карт, которые по этим адресам находились на самом деле остаются в памяти. Или это эквивалентный код?
Вот что я хотел спросить.
0
 Аватар для igorrr37
2863 / 2011 / 988
Регистрация: 21.12.2010
Сообщений: 3,716
Записей в блоге: 15
25.03.2022, 15:27 12
Там всё просто. Сколько раз вызвали в программе new, столько же раз надо вызвать в программе и delete. Ну и для new[] / delete[] так же.
0
 Аватар для PencilTorch
90 / 60 / 32
Регистрация: 06.08.2020
Сообщений: 219
25.03.2022, 15:48 13
Цитата Сообщение от realalexandro Посмотреть сообщение
3. CARD* DECK::drawCrd(int i) сначала копирует по индексу указатель во временную переменную, потом только зануляет соответствующий указатель в DECK и возвращает тот временный сохр. указатель т.е. мы получаем на выходе адрес карты, которую "вытащили" из колоды, он попадает в SUBSET, и одновременно из массива DECK мы его убираем..
Ну идея вроде ясна, но в вашем коде в drawCrd создается временный указатель в который копируется адрес где лежит карта. Потом этот адрес зануляется и в итоге возвращается указатель на зануленный адрес.
0
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
25.03.2022, 16:40  [ТС] 14
Цитата Сообщение от PencilTorch Посмотреть сообщение
Ну идея вроде ясна, но в вашем коде в drawCrd создается временный указатель в который копируется адрес где лежит карта. Потом этот адрес зануляется и в итоге возвращается указатель на зануленный адрес.
Нет, почему? Сначала адрес сохраняется в локальной переменной функции, т.е. мы чётко запомнили по какому адресу лежит объект карты, этот адрес мы и вернём через возврат по значению локальной переменной!
Только потом "исходник" этого адреса массиве deck[] зануляется, так что ничего не пропадает.
Мы же возвращаем сохранённый адрес определённого участка в памяти (то же самое что указатель на некоторую область).
А второй указатель при этом перестаёт указывать на эту область т.е. адрес который в нём хранился становится nullptr.
Всё прекрасно работает!
0
фрилансер
 Аватар для Алексей1153
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
25.03.2022, 16:42 15
realalexandro, PencilTorch, вот, слегка упростил и приукрасил. В логику не вникал
C++
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
#include <iostream>
#include <ctime>
#include <cassert>
#include <vector>
#include <optional>
 
using std::cout;
using std::endl;
 
enum class Fvalues { EMPTY = 0, TWO = 2, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE };
enum class Suits { NOSUIT = 0, HEARTS = 3, DIAMONDS, CLAWS, SPADES };
 
class CARD
{
public:
    Fvalues fval{};
    Suits suit{};
    
public:
    bool isNull()const
    {
        return fval == Fvalues::EMPTY && suit == Suits::NOSUIT;
    }
    
    void display() const
    {
        if (isNull())
        {
            cout << static_cast<char>(254) 
                << " " << static_cast<char>(254) << endl;
        }
     
        // выводим достоинство карты
        switch (fval)
        {
            default: {cout << "Unknown face value!\n";}break;
            case Fvalues::TWO  :{cout << "2";}break;
            case Fvalues::THREE:{cout << "3";}break;
            case Fvalues::FOUR :{cout << "4";}break;
            case Fvalues::FIVE :{cout << "5";}break;
            case Fvalues::SIX  :{cout << "6";}break;
            case Fvalues::SEVEN:{cout << "7";}break;
            case Fvalues::EIGHT:{cout << "8";}break;
            case Fvalues::NINE :{cout << "9";}break;
            case Fvalues::TEN  :{cout << "T";}break;
            case Fvalues::JACK :{cout << "J";}break;
            case Fvalues::QUEEN:{cout << "Q";}break;
            case Fvalues::KING :{cout << "K";}break;
            case Fvalues::ACE  :{cout << "A";}break;
        }
 
        // выводим масть карты
        switch (suit)
        {
            default: {cout << "Unknown suit!\n";}break;
            case Suits::HEARTS  :{cout << "3";}break;
            case Suits::DIAMONDS:{cout << "4";}break;
            case Suits::CLAWS   :{cout << "5";}break;
            case Suits::SPADES  :{cout << "6";}break;
        }
    }
};
 
class DECK
{
public:
    std::vector<CARD> deck;
 
    DECK(bool init_default)
    {
        if(init_default)
        {
            deck.reserve(52);
            for(auto su:{Suits::HEARTS,Suits::DIAMONDS, Suits::CLAWS, Suits::SPADES })
            {
                for (auto fv:{  Fvalues::TWO,Fvalues::THREE,Fvalues::FOUR,Fvalues::FIVE,
                                Fvalues::SIX,Fvalues::SEVEN,Fvalues::EIGHT,Fvalues::NINE,
                                Fvalues::TEN,Fvalues::JACK,Fvalues::QUEEN,Fvalues::KING,Fvalues::ACE})
                {
                    deck.push_back({fv,su});
                }
            }
        }
    }
 
    void display() const
    {
        for (const auto& card:deck)
        {
            if (card.isNull())
            {
                cout << "__\n";
            }
            else
            {
                card.display();
            }
        }
    }
    
    void Swap(const size_t ind1, const size_t ind2)
    {
        assert(ind1<deck.size());
        assert(ind2<deck.size());
        std::swap(deck[ind1],deck[ind2]);
    }
    void Shuffle() {
        int ind1{}, ind2{};
        srand(static_cast<unsigned int>(time({})));
        for (size_t j = 0; j <= 300;)
        {
            ind1 = rand() % deck.size();
            ind2 = rand() % deck.size();
            if (ind1 == ind2)
            {
                continue;
            }
 
            Swap(ind1, ind2);
            j++;
        }
    }
    
    std::optional<CARD> drawCrd(const size_t index)
    {
        std::optional<CARD> card;
        
        if (index<deck.size())
        {
            card=deck[index];
            deck.erase(deck.begin()+index);
        }
        return card;
    }
};
 
class SUBSET:public DECK
{
public:
    SUBSET():DECK(false)
    {
    }
 
    void fillSubset(DECK& src_deck,const size_t N)
    {
        deck.reserve(N);
        deck.clear();
        for (size_t i = 0; i < N && i< src_deck.deck.size(); i++)
        {
            auto card=src_deck.drawCrd(i);
            if(!card)break;
            deck.push_back(*card);
        }
    }
 
    void display() const
    {
        if(deck.size())
        {
            for (const auto& card:deck)
            {
                card.display();
            }
            cout << endl;
        }
    };
};
 
int main()
{
    DECK d1{true};
    d1.display();
    cout << endl;
    
    d1.Shuffle();
    d1.display();
    cout << endl;
 
    SUBSET sub1;
    sub1.fillSubset(d1,6);
    sub1.display();
    cout << endl;
    
    d1.display();
    cout << endl;
 
    return 0;
}
1
0 / 0 / 0
Регистрация: 25.07.2020
Сообщений: 36
25.03.2022, 16:49  [ТС] 16
Цитата Сообщение от igorrr37 Посмотреть сообщение
Там всё просто. Сколько раз вызвали в программе new, столько же раз надо вызвать в программе и delete. Ну и для new[] / delete[] так же.
Тогда получается надо так, если я правильно понял?:
C++
1
2
 for (int i = 0; i < 52; i++) delete deck[i];
   delete[] deck;
Хочется уже как то завершить с этой темой поскорей.

Добавлено через 6 минут
Алексей1153, Спасибо, будем учиться!
Но я себе задачу поставил сделать без Vector-ов, просто с массивами и указателями т.е. по-колхозному))
0
фрилансер
 Аватар для Алексей1153
5863 / 5402 / 1106
Регистрация: 11.10.2019
Сообщений: 14,435
25.03.2022, 16:58 17
Цитата Сообщение от realalexandro Посмотреть сообщение
просто с массивами и указателями
да не вопрос. Напиши класс (-шаблон) - аналог вектора
0
25.03.2022, 16:58
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.03.2022, 16:58
Помогаю со студенческими работами здесь

Ошибка при создании динамического массива
Требуется создать динамический массив, размер которого не известен. Сам массив объявлен глобальным...

Ошибка при создании динамического массива
При создании динамического массива (матрицы) возникает ошибка Сам процесс создания выведен в...

Ошибка при создании динамического массива.
Все доброго времени. Начал создавать одномерный массив, потом решил его преобразовать в...

Ошибка при создании динамического двумерного массива
Ткните пальцем, что не так. Только учусь, делаю лабы, камнями не забрасывайте. Смотрел шаблоны...

Ошибка при создании двумерного динамического массива
Нужно создать матрицу (на основе динамики (язык СИ)). В чем ошибка? void main() { int...

Access Violation при повторном заполнении динамического массива
Здравствуйте, есть код парсинга некоторой информации с сайта. type RRecord = record ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Новые блоги и статьи
[Golang] 121. Best Time to Buy and Sell Stock
alhaos 28.01.2025
В этой задаче мы получаем слайс целых чисел, которые означают цену акции в разные моменты времени, и должны вернуть максимально возможную прибыль от купли продажи акции. / / . . .
Проектирование и моделирование
hw_wired 28.01.2025
Введение в моделирование Моделирование представляет собой один из фундаментальных методов научного познания, который позволяет изучать объекты и явления через создание их упрощенных аналогов. В. . .
Алгоритмы и исполнители
hw_wired 28.01.2025
Введение в алгоритмы В современном мире информационных технологий алгоритмы играют основополагающую роль в решении различных задач и автоматизации процессов. Алгоритм представляет собой точную. . .
Хранение информации
hw_wired 28.01.2025
Введение: Роль систем хранения информации в современном мире В современную эпоху цифровых технологий эффективное хранение информации становится одним из ключевых факторов успешного развития любой. . .
Обработка числовой информации
hw_wired 28.01.2025
Введение в обработку числовой информации В современном мире обработка числовой информации стала неотъемлемой частью как профессиональной деятельности, так и повседневной жизни. Электронные таблицы. . .
Мультимедиа
hw_wired 28.01.2025
Введение в мультимедийные технологии В современном мире мультимедийные технологии стали неотъемлемой частью нашей жизни, проникнув во все сферы человеческой деятельности. Термин "мультимедиа". . .
Обработка текстовой информации
hw_wired 28.01.2025
Введение в обработку текстовой информации В современном мире обработка текстовой информации играет фундаментальную роль в различных сферах человеческой деятельности. Текстовые редакторы стали. . .
Обработка графической информации
hw_wired 28.01.2025
Введение в компьютерную графику Компьютерная графика стала неотъемлемой частью современного цифрового мира, пройдя впечатляющий путь развития от простейших черно-белых изображений до сложных. . .
Python в Алгоритмике: Решение задач
hw_wired 28.01.2025
Введение в Python и Алгоритмику В современном мире программирование стало неотъемлемой частью образования и профессионального развития. Python зарекомендовал себя как один из самых популярных и. . .
Компьютер как универсальное устройство для работы с информацией
hw_wired 28.01.2025
Введение в устройство компьютера Компьютер представляет собой универсальное электронное устройство, предназначенное для автоматической обработки информации. В современном мире компьютер стал. . .
Информация и информационные процессы
hw_wired 28.01.2025
Понятие информации и ее виды В современном мире информация является одним из фундаментальных понятий, пронизывающих все сферы человеческой деятельности. Под информацией понимают любые сведения об. . .
Алгоритмика
hw_wired 28.01.2025
Введение: Основы алгоритмики и её роль в информатике В современном мире программирование и алгоритмическое мышление стали неотъемлемой частью образования и профессиональной деятельности. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru