Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 20, средняя оценка - 4.85
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
#1

Выделение памяти под массив структур - C++

06.04.2012, 11:13. Просмотров 2663. Ответов 15
Метки нет (Все метки)

Возникла проблема в том что не могу никак эфективно реализовать выделение памяти под массив структур, более того: единожды выделив память я её даже освободить не могу!

Условие задания: есть класс машин (у каждой свой номер) и класс водителей (у каждого имя и фамилия). В каждом из классов есть структура TLog* что имеет вид
C++
1
2
3
4
5
    struct TLog
     {
       string m_Date;
       CCar* m_Car;//либо CDriver* m_Driver
     };
Структура позволяет хранить в классе машин кто на них ездил, а в классе пользователей на каких машинах они ездили. Класс CDriver и CCar практически полностью идентичны и проблема с выделением памяти у меня возникла и там и там, но я остановлюсь только на CCar. Выделяю я память под структуру TLog в функции usingCar (описана внизу перед main):
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
//почти полная копия CCar, можно просто пролистать его
class CDriver
{
public:
    CDriver(const string& name, const string& surname)
    {
        m_Name= name;
        m_Surname= surname;
        m_LogNr= 0;
        m_Log= new TLog[1];
    }
 
 
 
    ~CDriver()
    {
        delete [] m_Log;
    }
 
    void usingCar(CCar* what, const string& when)
     {
         if(m_LogNr==0)
         {
            m_Log[0].m_Car=what;
            m_Log[0].m_Date=when;
 
            m_LogNr++;
            return;
        }
 
        TLog* tempLog=new TLog[m_LogNr+1];
        memmove(tempLog,m_Log,sizeof(TLog)*m_LogNr);
        m_Log=tempLog;
 
        m_Log[m_LogNr].m_Date=when;
        m_Log[m_LogNr].m_Car=what;
        m_LogNr++;
 
 
 
        qsort(m_Log, m_LogNr, sizeof(TLog), CDriver::compareDriversInside);
     }
 
 
     string getName() const
     {
         return (m_Name+" "+m_Surname);
     }
 
    static int compareDriversInside(const void* _first, const void* _second)
    {
        const TLog* first= (TLog*) _first;
        const TLog* second= (TLog*) _second;
        return first->m_Date.compare(second->m_Date);
    }
 
    friend ostream& operator <<(ostream& os,const CDriver& base);
protected:
    struct TLog
     {
       string m_Date;
       CCar* m_Car;
     };
 
    string m_Name;
    string m_Surname;
    TLog* m_Log;
    int m_LogNr;
 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
class CCar
 {
public:
   CCar(const string& RZ)
   {
       m_RZ=RZ;
       m_LogNr=0;
       m_Log= new TLog[1];///выделяю память под один элемент
   }
 
   ~CCar()
   {
       delete []m_Log;
   }
 
   void usingCar(CDriver* who, const string& when);///здесь и возникает проблема, описана чуть ниже
 
    const string& getRZ() const
    {
        return m_RZ;
    }
 
    static int compareCarsInside(const void* _first, const void* _second)
    {
        const TLog* first= (TLog*) _first;
        const TLog* second= (TLog*) _second;
        return first->m_Date.compare(second->m_Date);
    }
 
    friend ostream& operator <<(ostream& os,const CCar& base);
protected:
    struct TLog
     {
       string m_Date;//когда
       CDriver* m_Driver;//кто
     };
    string m_RZ;//регистрационный номер авто
    TLog* m_Log;//структура с записями кто и когда водил авто
    int m_LogNr;//сколько записей о использовании авто у нас есть
 };
 
 ostream& operator <<(ostream& os,const CDriver& base)
{
    os<<base.m_Name<<" "<<base.m_Surname<<endl;
    for(int i=0;i<base.m_LogNr;i++)
     os<<base.m_Log[i].m_Date<<": "<<base.m_Log[i].m_Car->getRZ()<<endl;
    return os;
}
 
ostream& operator <<(ostream& os,const CCar& base)
{
    os<<base.m_RZ<<endl;
    for(int i=0;i<base.m_LogNr;i++)
     os<<base.m_Log[i].m_Date<<": "<<base.m_Log[i].m_Driver->getName()<<endl;
 
    return os;
}
 
 
 
 
//проблемы появляются здесь так как здесь происходит утечка памяти
void CCar::usingCar(CDriver* who, const string& when)
{
    if(m_LogNr==0) //память уже была выделена конструктором для первого элемента
    {
        m_Log[0].m_Driver=who;
        m_Log[0].m_Date=when;
 
        m_LogNr++;
        return;
    }
    //иначе нужно выделить память под массив на 1 больше чем был
    
    TLog* tempLog=new TLog[m_LogNr+1];
    memmove(tempLog,m_Log,sizeof(TLog)*m_LogNr);
    //m_Log не удаляется
    m_Log=tempLog;
 
    m_Log[m_LogNr].m_Date=when;
    m_Log[m_LogNr].m_Driver=who;
    
    m_LogNr++;
}
 
 
int main()
{
    CCar* car= new CCar("XYZ-123");
    CDriver* driver=new CDriver("Homer", "Simpson");
    CDriver* driver2= new CDriver("Magre", "Simpson");
    car->usingCar(driver,"3");
    car->usingCar(driver,"1");
    car->usingCar(driver2,"2");
    cout<<*car;
    delete car;
}
Сейчас как видно там происходит явная утечка памяти так как старая память не освобождается! Подскажите как же здесь поступить?

Мои попытки решений:
Пытаюсь удалять старую память - программа вываливается (но почему же?):
показать код

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//проблемы появляются здесь так как здесь происходит утечка памяти
void CCar::usingCar(CDriver* who, const string& when)
{
    if(m_LogNr==0) //память уже была выделена конструктором для первого элемента
    {
        m_Log[0].m_Driver=who;
        m_Log[0].m_Date=when;
 
        m_LogNr++;
        return;
    }
    //иначе нужно выделить память под массив на 1 больше чем был
 
    TLog* tempLog=new TLog[m_LogNr+1];
    memmove(tempLog,m_Log,sizeof(TLog)*m_LogNr);
    delete [] m_Log; //ОСВОБОЖДЕНИЕ ПАМЯТИ, приводит к тому что программа вываливается
    m_Log=tempLog;
 
    m_Log[m_LogNr].m_Date=when;
    m_Log[m_LogNr].m_Driver=who;
 
    m_LogNr++;
}

Любопытно что этот код крашиться и в vs2010, но если поменять delete [] m_Log на this->~CCar() (а деструктор как видно в коде какраз таки и выполняет delete [] m_Log) то код не будет вываливаться в vs2010 (в minigw всё равно будет вываливаться). Разве это не чудо? Но сдаваться это всё будет на g++ так что vs2010 здесь не аргумент.

Пытаюсь увеличивать ту память что была уже у меня через realloc:
realloc возвращает NULL (первый раз у меня такое чтобы он не справился)
показать код

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//проблемы появляются здесь так как здесь происходит утечка памяти
void CCar::usingCar(CDriver* who, const string& when)
{
    if(m_LogNr==0) //память уже была выделена конструктором для первого элемента
    {
        m_Log[0].m_Driver=who;
        m_Log[0].m_Date=when;
 
        m_LogNr++;
        return;
    }
    //иначе нужно выделить память под массив на 1 больше чем был
 
    m_Log= (TLog*) realloc(m_Log,sizeof(m_Log[0])*(m_LogNr+1));
    
    if(!m_Log) //эта ветка выполняется
     cout<<"ERRROR! REALLOC RETURN 0!";
 
    m_Log[m_LogNr].m_Date=when;
    m_Log[m_LogNr].m_Driver=who;
 
    m_LogNr++;
}


Так что же мне делать, подскажите пожалуйста
Подозреваю что проблемы со структурой из-за того что в структуре строка string, но какое-то же решение должно быть...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
06.04.2012, 11:13
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Выделение памяти под массив структур (C++):

Выделение памяти под массив структур - C++
Добрый день. Подскажите в чём ошибка. #pragma hdrstop #pragma argsused #include &lt;stdio.h&gt; #include &lt;iostream&gt; struct...

Выделение динамической памяти под массив структур - C++
Здравствуйте! Пытаюсь выделить память под массив структур, считать с клавиатуры, вывести на экран и удалить выделенную память. Подскажите в...

Выделение динамической памяти под массив структур - C++
Добрый день, уважаемые! Пишу функцию выделения памяти под массив структур. Но программа не хочет правильно работать. подскажите пожалуйста,...

Динамическое выделение памяти под массив структур (new/delete) - C++
Люди добрые, подскажите пожалуйста, где в моем коде закралась ошибка? Интернет перечитал, во всех примерах код, подобный моему работает, а...

Выделение памяти под массив - C++
Требуется выделить память под матрицу. Будет ли следующий код работать корректно? //глобальная область int x_size = 3; ...

Выделение памяти под массив - C++
в массиве данных хранять в последовательных ячейках памяти В программе, выделенна память под массив, для ввода данных пользователем. ...

15
kazak
3048 / 2369 / 160
Регистрация: 11.03.2009
Сообщений: 5,436
Завершенные тесты: 1
06.04.2012, 11:40 #2
Цитата Сообщение от Gepar Посмотреть сообщение
void usingCar(CCar* what, const string& when)
{
\\..
TLog* tempLog=new TLog[m_LogNr+1];
memmove(tempLog,m_Log,sizeof(TLog)*m_LogNr);
m_Log=tempLog;
m_Log[m_LogNr].m_Date=when;
m_Log[m_LogNr].m_Car=what;
\\..
}
А где освобождение памяти на которую указывал m_Log?

Добавлено через 1 минуту
Цитата Сообщение от Gepar Посмотреть сообщение
Пытаюсь увеличивать ту память что была уже у меня через realloc:
realloc можно вызывать, только если память вызывалась malloc/calloc.
0
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
06.04.2012, 11:56  [ТС] #3
Цитата Сообщение от kazak Посмотреть сообщение
А где освобождение памяти на которую указывал m_Log?
А я почему тему создал?
Пытаюсь освободить - программа вываливается сразу на месте же. Попробуйте скомпилировать и запустить код, он полностью компилируемый, там все что нужно для компиляции есть.

Добавлено через 1 минуту
Цитата Сообщение от kazak Посмотреть сообщение
realloc можно вызывать, только если память вызывалась malloc/calloc.
Так тоже не срабатывает, я уже пробовал, чуть позже могу и код показать когда будет malloc и realloc и вновь будет крешиться.
0
villu
203 / 204 / 4
Регистрация: 06.08.2011
Сообщений: 600
Записей в блоге: 1
06.04.2012, 11:58 #4
и правильно вываливается.
вот тут:
C++
1
 memmove(tempLog,m_Log,sizeof(TLog)*m_LogNr);
ты переносишь объекты простым копированием памяти. и при удалении старого контейнера вызываются деструкторы этих объектов и все указатели в "новых" объектах ... старые, то есть уже удаленные
0
kazak
3048 / 2369 / 160
Регистрация: 11.03.2009
Сообщений: 5,436
Завершенные тесты: 1
06.04.2012, 12:06 #5
Цитата Сообщение от villu Посмотреть сообщение
и при удалении старого контейнера вызываются деструкторы этих объектов
Так в том то и дело, что старый массив структур не удаляестя.
0
villu
203 / 204 / 4
Регистрация: 06.08.2011
Сообщений: 600
Записей в блоге: 1
06.04.2012, 12:09 #6
Цитата Сообщение от kazak Посмотреть сообщение
Так в том то и дело, что старый массив структур не удаляестя.
Цитата Сообщение от Gepar Посмотреть сообщение
Пытаюсь освободить - программа вываливается сразу на месте же.
а кто вообще научил так объекты копировать?
и почему вообще не использовать вектор, например.
0
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
06.04.2012, 13:07  [ТС] #7
Цитата Сообщение от villu Посмотреть сообщение
а кто вообще научил так объекты копировать?
Жизнь научила, ничего больше не работает кроме этого же Сейчас покажу с malloc и с тупо построчным копированием.

Цитата Сообщение от villu Посмотреть сообщение
и почему вообще не использовать вектор, например.
Нельзя, это не с 0 написано, половина функций и все переменные уже были сразу назначены, а имена декларированы. Я могу лишь дописывать функции и переменные. Вообще это кусок программы, на деле это бд, отдельные места которой из-за ограничений на функции что уже были добавлены и то что нельзя менять ни параметры, ни тип возвращающихся значений выглядят вот так например:
C++
1
m_Cars[i]->usingCar(const_cast<CDriver*>(&(FindDriver(fname,sname))),right.m_Cars[i]->m_Log[j].m_Date);
Я это называю конструктор копирования или просто ужас. Но да это хоть работает, а вот описанный мной выше код работает не корректно же.

Цитата Сообщение от kazak Посмотреть сообщение
realloc можно вызывать, только если память вызывалась malloc/calloc.
Меняю конструктор по умолчанию заменяю выделение через new на выделение через malloc. Конструктор выглядит так:
C++
1
2
3
4
5
6
   CCar(const string& RZ)
   {
       m_RZ=RZ;
       m_LogNr=0;
       m_Log= (TLog*) malloc (sizeof(TLog)*10);///выделяю память под ДЕСЯТЬ элементов умышленно с запасом
   }
функция using car так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//проблемы появляются здесь так как здесь происходит утечка памяти
void CCar::usingCar(CDriver* who, const string& when)
{
    if(m_LogNr==0) //память уже была выделена конструктором для первого элемента
    {
        m_Log[0].m_Driver=who;
        m_Log[0].m_Date=when; //вот здесь при первом же вызове функции программа крашиться, видать криво выделил память malloc для строки типа string
 
        m_LogNr++;
        return;
    }
   //ничего не выделяю, у меня места и так под 10 элементов
 
    m_Log[m_LogNr].m_Date=when;
    m_Log[m_LogNr].m_Driver=who;
 
    m_LogNr++;
}
Как видите вновь ошибка ...
0
kazak
3048 / 2369 / 160
Регистрация: 11.03.2009
Сообщений: 5,436
Завершенные тесты: 1
06.04.2012, 13:13 #8
malloc не вызывает конструкторов для объектов.
0
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
06.04.2012, 13:31  [ТС] #9
Добавлю что если заменить memmove на memcpy то ээфекта естественно не будет никакого.
Странно кстати что вот попробовал вручную копировать все-все-все элементы и после такого копирования память удалилась нормально, но это ведь долго будет выполнятся, особенно если у меня десяток тысяч записей будет ...
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
void CCar::usingCar(CDriver* who, const string& when)
{
    if(m_LogNr==0) //память уже была выделена конструктором для первого элемента
    {
        m_Log[0].m_Driver=who;
        m_Log[0].m_Date=when;
 
        m_LogNr++;
        return;
    }
    //иначе нужно выделить память под массив на 1 больше чем был
 
    TLog* tempLog=new TLog[m_LogNr+1];
 
    for(int i=0;i<m_LogNr;i++)
    {
        tempLog[i].m_Date=m_Log[i].m_Date;
        tempLog[i].m_Driver=m_Log[i].m_Driver;
    }
    delete [] m_Log;
    m_Log=tempLog;
 
    m_Log[m_LogNr].m_Date=when;
    m_Log[m_LogNr].m_Driver=who;
 
    m_LogNr++;
}
Ну и я по прежнему хочу узнать почему здесь realloc не перевыделяет память, malloc выделяет её некорректно, а memove и memcpy лажают?

Добавлено через 5 минут
Код полностью компилируемый, если кто не верит что тот же malloc не справляется с выделением памяти - скопируйте из начала темы код полностью и поменяйте выделение памяти в конструкторе CCar с new на malloc.

Добавлено через 1 минуту
Цитата Сообщение от kazak Посмотреть сообщение
malloc не вызывает конструкторов для объектов.
И ... ? Я же через malloc выделяю память под
C++
1
2
3
4
5
    struct TLog
     {
       string m_Date;//когда
       CDriver* m_Driver;//кто
     };
Это обычная структура, у неё нет конструкторов, а CDriver* это лишь указатель, объект существует и нужно лишь держать в памяти указатель на него.

Добавлено через 36 секунд
Или нужно вызывать как-то конструктор для string?

Добавлено через 9 минут
Или это получается что из-за того что у меня string в структуре я не смогу использовать malloc/realloc потому что не смогу явно вызывать конструктор string ?
0
ForEveR
В астрале
Эксперт С++
7983 / 4742 / 321
Регистрация: 24.06.2010
Сообщений: 10,543
Завершенные тесты: 3
06.04.2012, 13:54 #10
Gepar, Не используйте malloc/calloc/realloc/free при работе с классами.
1
kazak
3048 / 2369 / 160
Регистрация: 11.03.2009
Сообщений: 5,436
Завершенные тесты: 1
06.04.2012, 14:04 #11
Цитата Сообщение от Gepar Посмотреть сообщение
Или это получается что из-за того что у меня string в структуре я не смогу использовать malloc/realloc потому что не смогу явно вызывать конструктор string ?
Ну теоритически если выделять память через malloc, то конструктор придется ручками вызывать, и деструктор перед удалением тоже. Плюс, вспомнил, что сложные структуры данных бывают POD (Plain Old Data) и non-POD типов. Для non-POD типов сишные malloc, memmov и т.д. не подходят вообще, а string это non-POD класс.
1
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
06.04.2012, 14:49  [ТС] #12
Получается что кроме выделения время от времени нового массива через new побольше у мемя и вариантов то нет.
0
easybudda
Модератор
Эксперт CЭксперт С++
9693 / 5643 / 962
Регистрация: 25.07.2009
Сообщений: 10,847
06.04.2012, 15:16 #13
Цитата Сообщение от Gepar Посмотреть сообщение
Получается что кроме выделения время от времени нового массива через new побольше у мемя и вариантов то нет.
Ну, если не учитывать самый разумный совет в этой теме (использовать стандартные контейнеры), то правильно - без вариантов. Но я бы ещё дальше пошёл - если это серьёзная база данных с большим количеством записей и разнообразием выборок, так лучше настоящую СУБД использовать (mSQL, MySQL, PostgreSQL, etc...). Короче, по-хорошему переписать бы весь этот кришнаитский манускрипт, всё равно толком работать не будет...
1
Gepar
1177 / 533 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
06.04.2012, 17:32  [ТС] #14
Цитата Сообщение от easybudda Посмотреть сообщение
Ну, если не учитывать самый разумный совет в этой теме (использовать стандартные контейнеры), то правильно - без вариантов.
Ну я же уже писал что код не мой, stl туда нельзя засовывать нужно дописать лишь чтобы это работало.
У меня только один вопрос остался: почему после memmove и memcpy нельзя удалить память в этом случае?
Они ведь куски памяти копируют, притом копируют почти корректно - то что они скопировали можно потом нормально использовать, но нельзя удалить, но опять же таки почему? Давайте допустим что memmove сработала некорректно и не правильно скопировала данные в новый кусок памяти, но старый кусок памяти же должен оставаться таким же, верно? А он не остаётся судя по тому что после memove попытка удалить тот кусок памяти с которым поработал memove приводит к тому что программа нафиг вываливается. Я до этого читал и считал что memove копирует всё побайтово без всяких там буферов так что должна быть точная копия куска памяти же.
0
villu
203 / 204 / 4
Регистрация: 06.08.2011
Сообщений: 600
Записей в блоге: 1
06.04.2012, 17:38 #15
почему после memmove и memcpy нельзя удалить память в этом случае?
потому что они копируют БИНАРНОЕ представление объекта. Они копируют все указатели, но не выделенную память, а после, в деструкторах, память освобождается и получаются объекты с указателями, которые указывают вникуда.

Добавлено через 1 минуту
Ну я же уже писал что код не мой, stl туда нельзя засовывать нужно дописать лишь чтобы это работало.
Ну оно у тебя и так "лишь чтобы это работает" ...
1
06.04.2012, 17:38
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.04.2012, 17:38
Привет! Вот еще темы с ответами:

Выделение памяти под массив строк - C++
Вот нерабочий код и сейчас объясню, что пытаюсь сделать fstream f; f.open(&quot;in.txt&quot;, ios::in); int i=0, j=0; char...

Динамическое выделение памяти под массив - C++
Я хочу функцию использовать пару раз, количество команд будет уменьшаться, и &quot;n&quot; надо менять. Как написать не знаю. 12 или 13 строка. ...

Выделение памяти под двумерный массив С++ - C++
Гуру С++, подскажите новичку, как обратиться к элементу при следующем выделении памяти? 1)double **matr = new double * 2)double *matr...

Выделение памяти под двумерный массив - C++
Доброе время ! Проблема такая : препод задал задачу сортировки массива произвольного типа(char* int* или double*) и сказал, чтобы любой тип...


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

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru