Консультация № 175733
30.12.2009, 03:01
0.00 руб.
0 4 1
Здравствуйте, эксперты.

Что не правильно в моем коде?

Программу может хранить массив из произвольного числа объектов класса.
Но при попытке записать данные в класс, программа вылетает.

Оператор new - НЕ подходит, т. к. количество объектов массива может быть изменено в любой момент, и следовательно, память будет постоянно "перевысвобождаться".



Приложение:
##### CLASS: #####

class person
{
public:

void SetName(string name) { _name=name;}
void SetAge(int age) { _age =age; }
private:
string _name;
int _age;
};

##### MAIN FUNCTION #####

...
string name;
int age;
int count=0;

person* users=NULL;
for(;;)
{
cout << "Person " << count+1 << ": ";
cout << endl << "Age (0 to exit): ";
cin >> age;
if(age==0) break;
cout << "Name: "; cin >> name;
count++;
users=(person*)realloc(users, count*sizeof(person));

if(users==NULL)
{
cout << "Error allocating memory!" << endl;
exit(1);
}

// ЗДЕСЬ ВЫЛЕТАЕТ

users[count-1].SetName(name);
users[count-1].SetAge(age);
}
...
free(users);

###########

Обсуждение

Неизвестный
30.12.2009, 07:43
общий
это ответ
Здравствуйте, Иванов Андрей Владимирович.

Тут сразу есть одна большая ошибка с самостоятельными alloc-ами: если в структуре есть переменные классов с конструкторами, то вызова этих конструкторов не будет! Для того и нужен new, он, кроме того что память выделяет, он делает правильную инициализацию. Он запустит конструктор класса, а конструктор запустит конструкторы своих переменных, и так далее.

Это видно, если посмотреть под дебагом состояние users после первого realloc: у строчном указателе мусор: 0xcdcdcdcd, и при обращении в SetName, класс string воспримет это, как валидную строчку и начнёт копирование по битому адресу.

Если нужно постоянно перевыделять память, даже таким странным образом, оператор new тоже подходит, достаточно просто завести пару указателей, по одному хранить данные, по второму выделять новое пространство, потом через memcpy перегонять одно в другое (опять же, аккуратней с указателями!), realloc делает тоже самое, только "тупо" выделяя кусок памяти без какой-либо инициализации. Alloc-ами надо пользоваться только на примитивных стуктурах данных, не требующих инициализации и только в случае, если требуется скорость (в играх, например).

В приложении привёл работающий код с закомментированными старыми строками. Использую вектор для хранения данных. Вектор позволяет резервировать память под данные (функция reserve) что избавить от постоянного её выделения. А так же есть параметро (не помню) который отвечает за то, сколько памяти будет выделено за раз, когда память в векторе кончится(что-то я его в векторе не нашёл, но он точно был). Кроме того вектор даёт возможность простого доступа к данным через итераторы, или через оператор массива []. И не надо греть голову про память контейнера(только про память самих элементов).

Приложение:
#include <iostream>
#include <string>
#include <vector>
using namespace std;


class CPerson
{
public:

void SetName(string name) { _name=name;}
void SetAge(int age) { _age =age; }
private:
string _name;
int _age;
};

int main ( int argc, char *argv[])
{

string name;
int age;
int count=0;

//person* users=NULL;
CPerson* pUser=NULL;
vector<CPerson*> vUsers;
vector<CPerson*>::iterator it;
for(;;)
{
cout << "Person " << count+1 << ": ";
cout << endl << "Age (0 to exit): ";
cin >> age;
if(age==0) break;
cout << "Name: "; cin >> name;
count++;
//users=(person*)realloc(users, count*sizeof(person));
pUser = new CPerson;

// if(users==NULL)
if(pUser==NULL)
{
cout << "Error allocating memory!" << endl;
exit(1);
}

// ЗДЕСЬ ВЫЛЕТАЕТ

// users[count-1].SetName(name);
// users[count-1].SetAge(age);
pUser->SetName(name);
pUser->SetAge(age);
vUsers.push_back(pUser);
}

//free(users);
for(it = vUsers.begin(); it<vUsers.end(); it++)
delete(*it);
vUsers.clear();
Неизвестный
30.12.2009, 16:16
общий
>> Сандров Алекс

Спасибо за разъяснение. Теперь как-то неохота использовать realloc.
Неизвестный
31.12.2009, 04:24
общий
Сандров Алекс:
А так же есть параметро (не помню) который отвечает за то, сколько памяти будет выделено за раз, когда память в векторе кончится(что-то я его в векторе не нашёл, но он точно был).
Нет такого параметра. Емкость вектора - capacity(), при необходимости, увеличивается на 50%. Т.е. 0,1,2,3,4,6,9,13,...

Если же заранее известно сколько будет элементов то можно задать этот параметр в конструкторе при создании вектора.
Неизвестный
31.12.2009, 06:04
общий
Нет такого параметра.

Ага, нету, но у какого-то контейнера или массива я это встречал, можно было настроить приращение.
увеличивается на 50%

Вобщем, что-то в STL недодумано
Форма ответа