#cpp
Когда читал про выделение памяти с помощью new и ее освобождения, возник вопрос. Вот пример кода: const int size_m = 40; int main () { struct car { char *name; }; int QCar; cout << "How many car?"; cin >> QCar; car *pCar = new car[QCar]; for ( int i = 0; i < QCar; i++) { cout << "Car name: "; char temp [size_m]; char >> temp; char *ptemp = new char [strlen(temp)+1]; strcpy(ptemp,temp); pCar[i].name = ptemp; } cout << " Your auto"<< endl; for ( int i = 0; i < QCar; i++) { cout << pCar[i].name; } delete []pCar; system("pause"); return 0; } Является ли достаточным только это освобождение или нужно еще. Или вообще иначе. Просто выделение памяти дважды , а удаление одно.
Ответы
Ответ 1
Почти правильно :) Сейчас поясню, почему "почти". В C++ у каждого объекта есть такая вешь, как конструктор и деструктор. Если их не написать самому - их сгенерирует компилятор (все несколько сложнее, но я упрощаю как только могу). Деструктор вызывается при уничтожении объекта, так что вот тут delete []pCar; он будет вызван. Но вы не написали свой деструктор, а генерируемый понятия не имеет, что делать с name и потому не делает ничего. Так что надо дописать свой деструктор - struct car { char *name; ~car() { delete[]name; } }; В таком случае будет достаточно написать ваш delete []pCar; - и все будет корректно. Почти. Почему почти? потому что а вдруг вы забудете инициализировать name? И он будет показывать непонятно куда, а при удалении непонятно чего произойти может все, что угодно. Поэтому давайте допишем конструктор car(): name(nullptr) {} И все бы ничего, но ведь кто-то может (может, даже вы) написать где-то pCar->name = "ПЦ". И при попытке освобождения будут неприятности. Поэтому лучше делать не структуру, а класс: struct car { public: car(): name(nullptr) {} ~car() { delete[]name; } private: char *name; }; Готово? Почти. Потому что надо еще как-то теперь заставить name хранить строку. Опять же, делаем конструктор car(const char * str):name(nullptr) { if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } } и функцию-член для обращения к этой строке: const char* str() const { return name; } и оператор присваивания, чтоб name менять: car& operator=(const char * str) { delete[]name; if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } else name = nullptr; } Вот теперь можно считать готово. Можно дописать еще многое, но главное - уже на месте: struct car { public: car(const char * str = nullptr):name(nullptr) { if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } } ~car() { delete[]name; } const char str() const { return name; } car& operator=(const char * str) { delete[]name; if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } else name = nullptr; } private: char *name; }; А ваш код превращается в car *pCar = new car[QCar]; for ( int i = 0; i < QCar; i++) { cout << "Car name: "; char temp [size_m]; char >> temp; pCar[i] = temp; } cout << " Your auto"<< endl; for ( int i = 0; i < QCar; i++) { cout << pCar[i].str(); } delete []pCar; Примерно так - но только для начала :)Ответ 2
В приведенном коде есть две сущности, память под которые выделяется динамически: массив car и для каждого car - name. Необходимо освобождать память, выделенную и для того, и для другого. В текущем варианте освобождается память, выделенная для массива car. Память, выделенная для названий автомобилей не освобождается. Сделать это можно, например, так: for (int i = 0; (i < QCar); ++i) { delete[] pCar[i].name; } delete[] pCar; Однако, - для снижения вероятности ошибок и для освобождения памяти при возникновении исключений, - лучше было бы автоматизировать этот процесс: применить интеллектуальные указатели или создать свои классы с освобождающими память деструкторами (а для name можно использовать string).Ответ 3
Исключительно о том как добавить деструктор в Car: struct car { char *name; car()//для чистоты :name(nullptr) {} ~car() { delete name; } }; А так же неплохо бы ещё и конструктор копирования, так как вы храните указатель на область памяти, которую стоит копировать при копировании объекта. А если пройтись по коду, то многое не правильно (об указателе на внешнюю по отношению к классу car память понятно -- нарушение инкапсуляции). Относительно переменной temp: Это указатель на массив данных, который создаётся в стеке итерации цикла, при этом далее вы туда копируете данные, потом этот указатель приравниваете к внутренней переменной car. После итерации цикла память (та, на которую указывала переменная temp) и name указывает в никуда -- падение при доступе или когда-то, зависит от содержимого. Так же не освобождается переменная ptemp (strcpy копируется не указатель, а содержимое). -- утечка памяти. Используйте для строк std::string, замечательный класс, который умеет всё, что вам нужно (управление памятью, копирование, разделение памяти и т.д.)Ответ 4
for (int i = 0; i < QCar; i++) { cout << "Car name: "; char temp[size_m]; //char >> temp; char *ptemp = new char[strlen(temp) + 1]; strcpy(ptemp, temp); pCar[i].name = ptemp; delete[]ptemp; }Ответ 5
C pCar все правильно, а вот очистить ptemp забыли. pCar[i].name = ptemp; delete[] ptemp
Комментариев нет:
Отправить комментарий