Помогите разобраться
- Войдите на сайт для отправки комментариев
Вс, 27/11/2016 - 19:12
Программа работала, но на каждой итерации жрала память. Я переделал чтобы этого не было, но возникла проблема. Помогите разобраться почему после этой функции у меня меняется вектор математической модели, который вроде бы в ней никак не участвует (pModel->Vec), и вектор (VecR) из которой только берут его размер и подскажите как исправить.
Vector RParts (Vector Vec0, double t){ Serial.println("\tIn function RParts"); Serial.print("\t\t"); printVector(Vec0); Vector VecK(Vec0.n); VecK.VS[0] = sin(t);//+ return VecK; }
Вот полный код:
struct Vector { public: Vector(){ }; int n;//=1; float *VS; void Init(int an){ n=an; VS=new float [n]; } Vector(int an){ Init(an); } ~Vector(){ delete[] VS; } }; void printVector(Vector Vec){ for (int i=0; i<Vec.n; i++) Serial.println(Vec.VS[i]); Serial.println(); }; class AMatModel { public: Vector Vec; AMatModel(int an){ Vec.Init(an); }; // AMatModel(){ // delete[] Vec.VS; // }; //virtual Vector RParts(Vector Vec0, double t)=0; }; class MatModel : public AMatModel{ public : MatModel(int an):AMatModel(an){ }; Vector RParts (Vector Vec0, double t){ Serial.println("\tIn function RParts"); Serial.print("\t\t"); printVector(Vec0); Vector VecK(Vec0.n); VecK.VS[0] = sin(t);//+ return VecK; } }; class AInt { public: MatModel *pModel; double h; Vector VecR, VecI; virtual Vector Run(MatModel *pModel, double t, double tk)=0; }; class RInt : public AInt { public: Vector Run(MatModel *pModel, double t, double tk) { Serial.println("In function Run"); Serial.print("\t"); printVector(pModel->Vec); VecI.Init(pModel->Vec.n); VecR.Init(pModel->Vec.n); for (int i=0; i< VecR.n;i++) VecR.VS[i] = pModel->Vec.VS[i]; Serial.print("\t"); printVector(VecR); do { VecI = pModel->RParts(VecR, t); Serial.print("\t"); printVector(VecR); Serial.print("\t"); printVector(pModel->Vec); for (int i = 0; i<VecR.n; i++) { VecR.VS[i] += h * VecI.VS[i]; Serial.print("\t"); printVector(VecR); } t += h; } while (t<=tk-h); return VecR; }; }; MatModel MM(1); RInt Integr; Vector Vec; double t0, tk, dt; void setup() { // put your setup code here, to run once: Serial.begin(9600); for (int i=0; i<MM.Vec.n;i++) MM.Vec.VS[i]=-1; t0=0; tk=1; dt=0.1; } void loop() { // put your main code here, to run repeatedly: Vec.Init(1); // int l=0; // if (l==0){ // for (int i=0; i<MM.Vec.n;i++) // MM.Vec.VS[i]=-1; // l=1; // } // Serial.print("l="); // Serial.println(l); Integr.h=0.02; //Vec.VS[0]=0; Serial.println("Vector with coordinates:"); printVector(MM.Vec); Serial.println("Vector with coordinates:"); printVector(MM.Vec); Vec=Integr.Run(&MM,t0, t0+dt ); Serial.println("Vector with coordinates after integr:"); printVector(MM.Vec); for (int i=0; i<Vec.n;i++) MM.Vec.VS[i]=Vec.VS[i]; // Serial.print("Time:\t"); //Serial.println(millis()); delay(500); t0=t0+dt; Serial.print("t0="); Serial.println(t0); printVector(MM.Vec); Serial.print("-Cosinus= "); Serial.println(-cos(t0)); Serial.print("Free memory:"); Serial.println(memoryFree()); delete[] Vec.VS; Serial.print("Free memory:"); Serial.println(memoryFree()); } extern int __bss_end; extern void *__brkval; // Функция, возвращающая количество свободного ОЗУ (RAM) int memoryFree() { int freeValue; if((int)__brkval == 0) freeValue = ((int)&freeValue) - ((int)&__bss_end); else freeValue = ((int)&freeValue) - ((int)__brkval); return freeValue; }
А что, сейчас не жрёт? Жрёт за милую душу. Только при этом она ещё и всё в кучу сваливает.
Ну-ну ...
Переписать с нуля.
Не обижайтесь, но то, что здесь написано является полным бредом. Вот посмотрите пример (и это только одна маленькая проблемка).
Начало моего примера - это кусок Вашего кода с объявлением классов без каких либо изменений, кроме того, что я вставил две печати, который обозначил комментарием /**********************/, чтобы их было видно.
В конце (функция setup) я написал простенькую программку, посмотрите сами.
И что же она печатает?
Любуемся:
Сначала всё нормально - запросила память, потом напечатала вектор (причём, правильно напечатала).
А вот дальше (строка 7) она вдруг освобождает память (ещё до выхода из printVector!!!!) Опаньки, а кто ж её просил? Значит, после выхода из printVector память вектора уже освобождена и на её место кто хочет может писать что хочет!
Более того, при выходе из setup она ещё раз освобождает уже освобождённую память (последняя строка)!
Вы видите, что тут мешанина и бред? И с памятью может твориться всё, что угодно? Коль скоро она освобождена, то в неё можно писать что угодно, а при повторном освобождении ...
Виноват не printVector, виновата Ваша организация работы с памятью, поэтому точно такие же фокусы будут и с другими функциями. printVector только один из возможных примеров. Проблема глобальна по всей программе.
Так что не обижайтесь, но поправить в данном случае == переписать полностью.
Так с памятью в классах не работают.
Спасибо за разъяснение. Не могли бы вы посоветовать что надо прочитать для правильной работы с памятью в классах?
Ключевые слова: "коструктор копирования" и "перегрузка оператора присваивания". Поищите.
Дело в том, что Вы передаёте экземпляр класса по значению. При это создаётся новая копия экземпляра и старая целиком копируется в новую. Но ведь у Вас в классе указатель! Так вот именно он и копируется! Т.е. не выделяется новая память, не копируется, а просто копируется указатель. В результате указатель вновь созданной копии экземпляра указывает не на собственный массив, а на массив оригинального экземпляра. понимаете. Потом, когда копия уничтожается (перед выходом из функции) вызывается деструктор, который освобождает паямть. Он и освобождает память оригинального экземпляра, т.к. собственную память для копии никто вообще-то и не запрашивал.
Этим объясняется странное поведение в примере, который я приводитл.
точно такая же беда возникает не только при передаче по значению, а ещё и при присваивании. Вот объявили Вы Vector a, b; Поработали с ними, а потом решили присвоить один другому (a = b). Возникнет точно такая же проблема - при присваивании b будет полностью скопирован в a. Т.е. указатель в a станет рвным указателю в b. Они будут указывать на одну и ту же память (запрошенную для b), а про память, запрошенную для a просто забудут.
давайте я покажу это на простейшме примере, а то у Вас класс довольно сложный и там менее понятно.
Вот, смотрите такой пример:
он печатает
давайте разбираться
1. проблема присваивания
В строке 26 мы создали два экземпляра. Конструктор любезно сообщил нам адреса запрошенных кусков памяти. Потом мы напечатали эти экземпляры (строка 27 программы и строка 4 печати). Видим. что всё в порядке, адреса правильные. Так?
Теперь мы присваиваем в a = b; строке 28 и снова печатаем (строки 29 и 5) и что видим в строке 5 выдачи? Массивы имеют одинаковый адрес 654! А когда пришла пора экземплярам удаляться, оба деструктора освободили именно этот адрес. Про оригинальный адрес массива из a (642) просто забыли!
Понятна проблема?
2. проблема передачи по значению
Здесь я печатаю адрес самого экземпляра и адрес запрошенного для него массива. Не буду так подробно, но заметьте, то, что печатается изнутри функции показывает другой адрес экземпляра (т.е. локальный экземпляр для функции честно создан!) но у этого локального экезмпляра адрес массива точно такой же, какой был и о оригинала. При выходе из функции локальный экземпляр уничтожается и деструктор освобождает память его массива. Т.е. оригинальный (объявленный до функции) экземпляр остался без массива - его освободили.
Разобрались? Понимаете суть проблемы?
Эти проблемы имеют общий корнеь - а именно тупое копирование оригинального экземпляра в новый при присваивании и передаче в функцию.
Для того, чтобы проблему решить, нужно самому задать процедуру присваивания и создания экземпляра. И там написать как нам надо. Именно это делается оператором присваивания и конструктором копирования.
Посмотрите тот же самый пример, в котором обе проблемы решены. Код вроде прокомментирован, разберётесь
Выдаёт
Как видите ни проблемы присваивания, ни проблемы копирования больше нет.
Спасибо большое, за ваше потраченное время. Вы объяснили все довольно понятно и лакончично. Без вашей помощи я бы скорее всего и не разобрался.
Память по-любому надо починить, но Вы бы ещё подумали, а надо ли Вам так часто пользоваться передачей параметров по значению. Ну, вот функция printVector - ей нужно вектор напечатать, и Вы передаёте по значению, т.е. ради простой печати Вы создаёте новый экземпляр, должны запросить новую память, скопировать её. И всё это ради простой печати. Оно Вам надо? Передавайте по ссылке или указатель, и никаких новых экземпляров, никаких запросов памяти - тишь, да гладь.