Debag Vs Release

Vadik(R)

Well-known member
12.12.2007
469
0
#1
Добрый вечер-ночи, уважаемые форумчане.
Недавно наткнулся на такую интересную вещь, которую никак объяснить не могу. Вот код:
C++:
#include <iostream>
#include <locale>

using namespace std;

int max(int a, int b)
{
return a > b ? a : b;
}

class Polynom
{
public:
Polynom()
{
n = 1;
k = (int*) malloc(n * sizeof(int));
k[0] = 0;
}

Polynom(int n, int k[])
{
this->n = n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, k, this->n * sizeof(int));
}

Polynom& operator=(Polynom &other)
{
cout << "Start operator=" << endl;
this->n = other.n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, other.k, this->n * sizeof(int));
cout << "End operator=" << endl;
return *this;
}

~Polynom()
{
cout << "Start free k" << endl;
free(k);
cout << "End free k" << endl;
}

int rank()
{
return n;
}

int operator[] (int index)
{
return k[index];
}

Polynom sum(Polynom &a, Polynom &b)
{
int i;
int* k;
int n;
Polynom c;
n = max(a.rank(), b.rank());
k = (int*) malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
k[i] = 0;
if (i < a.rank())
{
k[i] += a[i];
}
if (i < b.rank())
{
k[i] += b[i];
}
}
c = Polynom(n, k);
free(k);
return c;
}

void Print()
{
int i;
for (i = 0; i < n; i++)
{
if (i)
{
printf("+ ");
}
printf("%d * a ^ %d ", k[n - i - 1], n - i - 1);
}
printf("\r\n");
}

private:
int* k;
int n;
};

int main()
{
int k[10];
Polynom a;
Polynom b;
k[0] = 1;
k[1] = -2;
k[2] = 1;
a = Polynom(3, k);
k[0] = -1;
k[1] = 1;
b = Polynom(2, k);
a = a.sum(a, b);
a.Print();
return 0;
}
Тут реализуется класс многочлен, где задаётся его степень и сами коэффициенты. Делается динамическим выделением памяти.
Для примера создал два многочлена:
1: x^2 - 2x + 1
2: x - 1
И решил их сложить.
Так вот, проблема вся в том, что если программа в режиме Release, то он их отлично складывает и выводит результат на экран.
А вот если она в Debug, то выдаётся ошибка:
На_форум.png
Как такое происходит? Как этого избежать? Можно в деструкторе закомментировать //free(k); , тогда ошибка выдаваться не будет, но будет утечка памяти. Прошу совета, как быть в такой ситуации, что в коде не так? С помощью cout'ов я понял, что ошибка выдаётся именно в деструкторе, но больше ничего понять не могу.
 

Вложения

R

rrrFer

#2
Я вообще не понимаю как этот код может работать.
Это: c = Polynom(n, k);
как так ?

А еще это:
a = Polynom(3, k);
это:
b = Polynom(2, k);
и вот это:
a = a.sum(a, ;);

Ты не понимаешь, что тут:
Polynom a;
Polynom b;

ты уже создал 2 объекта. А тут: Polynom(2, k); ты создал еще один объект.
Нельзя просто так взять и присвоить.

Что делает sum мне вообще не понятно. Если она не статическая, то нахрена она принимает 2 аргумента. Если принимает 2 аргумента - то зачем ей this? - почему не статическая?

Код:
#include <iostream>
#include <locale>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

using namespace std;

int max(int a, int b)
{
return a > b ? a : b;
}

class Polynom
{
public:
Polynom()
{
n = 1;
k = (int*) malloc(n * sizeof(int));
k[0] = 0;
}

Polynom(int n, int k[])
{
this->n = n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, k, this->n * sizeof(int));
}

Polynom& operator=(Polynom &other)
{
cout << "Start operator=" << endl;
this->n = other.n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, other.k, this->n * sizeof(int));
cout << "End operator=" << endl;
return *this;
}

~Polynom()
{
cout << "Start free k" << endl;
free(k);
cout << "End free k" << endl;
}

int rank()
{
return n;
}

int operator[] (int index)
{
return k[index];
}

static Polynom sum(Polynom &a, Polynom &b)
{
int i;
int* k;
int n;
n = max(a.rank(), b.rank());
k = (int*) malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
k[i] = 0;
if (i < a.rank())
{
k[i] += a[i];
}
if (i < b.rank())
{
k[i] += b[i];
}
}
Polynom c(n, k);
free(k);
return c;
}

void Print()
{
int i;
for (i = 0; i < n; i++)
{
if (i)
{
printf("+ ");
}
printf("%d * a ^ %d ", k[n - i - 1], n - i - 1);
}
printf("\r\n");
}

private:
int* k;
int n;
};

int main()
{
int k[10];

k[0] = 1;
k[1] = -2;
k[2] = 1;
Polynom a(3, k);
k[0] = -1;
k[1] = 1;
Polynom b (2, k);
Polynom res(Polynom::sum(a, b));
res.Print();
return 0;
}
Все равно это говнокод. Плохо. Я б на месте препода негодовал.

Добавлено:
Как такое происходит? Как этого избежать?
Очень просто такое происходит. Ты используешь, видимо, компилятор майкрософта.

дебаг и релиз - это совсем разные штуки и работают они по-разному. Но если ты пишешь более менее нормальный код - то и поделки майкрософта сработают предсказуемо (их уже отладили на этом), но если же ты говнокодишь, да так, что гейтс представить себе такого не может, то кнешно компилятору майкрософта ничего не остается кроме как ориентироваться на пятна на солнце, да фазу луны.

Используй уже gcc и прочитай нормальные книжки по плюсам (ссылки есть на форуме, я лично за ними слежу :D ) - http://codeby.net/forum/threads/51900.html
 

Vadik(R)

Well-known member
12.12.2007
469
0
#3
r04, приношу свои извинения. Я только сейчас увидел, что Вы код подправили. До этого думал, Вы просто процитировали его.

Насчёт статической функции sum - понял. Вполне понимал, что быдлокод. Но не думал, что настолько, что будет вылетать. А вот про то, что "нельзя просто так взять и присвоить" - никак не могу успокоиться.
Неужели между:
C++:
Polynom c(n, k);
и:
C++:
Polynom c;
c = Polynom(n, k);
есть разница, и ещё большая?

ты уже создал 2 объекта. А тут: Polynom(2, k); ты создал еще один объект.
Нельзя просто так взять и присвоить.
Почему нельзя просто взять и присвоить? Как же тогда работают всякие QString?
Мы же всегда может сделать, что-то типа:
QString str1;
QString str2;
QString str3;
str1 = "a";
str2 = "b";
str3 = str1 + str2;

Тут же ничего не вылетает. И присваивается объект в случае str3 = str1 + str2. Или я чего-то не понимаю?
Все равно это говнокод. Плохо.
Согласен, потому и пришёл сюда за помощью, чтобы разобраться.
Я б на месте препода негодовал.
К счастью препод уже не мой ;) После этой строчки может вы перестанете помогать, но это уже второй мой заказ, где я сталкиваюсь с этой проблемой, вот и заинтриговало самого :D
Что делает sum мне вообще не понятно.
По логике, она должна складывать два полинома и возвращать третий. Ну, как аналог str3 = str1 + str2.

Так мне в итоге как тогда стоило поступить? Для меня откровением стало уже:
Нельзя просто так взять и присвоить.
Как же быть? У меня единственный выход - только создание указателя на класс чтоль?
С sum попозже разберусь, вначале это интересует :)
 

Vadik(R)

Well-known member
12.12.2007
469
0
#4
r04, к сожалению, в Вашем код проблема по-прежнему осталась. Вот, даже сделал небольшую отладку:
C++:
Start constructor(n, k)
id = a
k = 00E127F0
End constructor(n, k)

Start constructor(n, k)
id = b
k = 00E1C1B8
End constructor(n, k)

Start constructor(n, k)
id = c
k = 00E1C260
End constructor(n, k)

Start free k
id = c
k = 00E1C260
End free k

Start free k
id = c
k = 00E1C260
На данный момент код таков:
C++:
#include <iostream>
#include <locale>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

using namespace std;

char c;

int max(int a, int b)
{
return a > b ? a : b;
}

class Polynom
{
public:
Polynom()
{
cout << "Start constructor()" << endl;
d = c++;
n = 1;
k = (int*) malloc(n * sizeof(int));
k[0] = 0;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End constructor()" << endl << endl;
}

Polynom(int n, int k[])
{
cout << "Start constructor(n, k)" << endl;
d = c++;
this->n = n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, k, this->n * sizeof(int));
//for (int i = 0; i < n; i++)
//	cout << this->k[i] << " " << k[i] << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End constructor(n, k)" << endl << endl;
}

Polynom& operator=(Polynom &other)
{
cout << "Start operator=" << endl;
//free(this->k);
this->n = other.n;
this->k = (int*) malloc(this->n * sizeof(int));
memcpy(this->k, other.k, this->n * sizeof(int));
//memcpy(&(this->k), &(other.k), this->n * sizeof(int));
//cout << this->k << endl;
//cout << other.k << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End operator=" << endl << endl;
return *this;
}

~Polynom()
{
cout << "Start free k" << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
free(k);
cout << "End free k" << endl << endl;
}

int rank()
{
return n;
}

int operator[] (int index)
{
return k[index];
}

static Polynom sum(Polynom &a, Polynom &b)
{
int i;
int* k;
int n;
n = max(a.rank(), b.rank());
k = (int*) malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
k[i] = 0;
if (i < a.rank())
{
k[i] += a[i];
}
if (i < b.rank())
{
k[i] += b[i];
}
}
Polynom c(n, k);
free(k);
return c;
}

void Print()
{
int i;
for (i = 0; i < n; i++)
{
if (i)
{
printf("+ ");
}
printf("%d * a ^ %d ", k[n - i - 1], n - i - 1);
}
printf("\r\n");
}

private:
char d;
int* k;
int n;
};

int main()
{
c = 'a';
int k[10];
k[0] = 1;
k[1] = -2;
k[2] = 1;
Polynom a(3, k);
k[0] = -1;
k[1] = 1;
Polynom b(2, k);
//Polynom::sum(a, b);
Polynom res(Polynom::sum(a, b));
//Polynom res;
//res = Polynom::sum(a, b);
//res.Print();
return 0;
}
Замечу только, что он становится рабочем и в дебаге и вызывает один деструктор при таком сочетании:
C++:
static Polynom& sum(Polynom &a, Polynom &b)
...
Polynom::sum(a, b);
То есть, sum возвращает ссылку. А Ваш случай, к сожалению, по-прежнему ошибку вызывает, почему-то для одного и того же объекта деструктор вызывается два раза ;)
 

Vadik(R)

Well-known member
12.12.2007
469
0
#5
Ещё один update. Присваивания всё-таки работают:
C++:
#include <iostream>
#include <locale>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

using namespace std;

char c;

int max(int a, int b)
{
return a > b ? a : b;
}

class Polynom
{
public:
Polynom()
{
cout << "Start constructor()" << endl;
d = c++;
n = 1;
k = (int*)malloc(n * sizeof(int));
k[0] = 0;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End constructor()" << endl << endl;
}

Polynom(int n, int k[])
{
cout << "Start constructor(n, k)" << endl;
d = c++;
this->n = n;
this->k = (int*)malloc(this->n * sizeof(int));
memcpy(this->k, k, this->n * sizeof(int));
//for (int i = 0; i < n; i++)
//	cout << this->k[i] << " " << k[i] << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End constructor(n, k)" << endl << endl;
}

Polynom& operator=(Polynom &other)
{
cout << "Start operator=" << endl;
//free(this->k);
this->n = other.n;
this->k = (int*)malloc(this->n * sizeof(int));
memcpy(this->k, other.k, this->n * sizeof(int));
//memcpy(&(this->k), &(other.k), this->n * sizeof(int));
//cout << this->k << endl;
//cout << other.k << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
cout << "End operator=" << endl << endl;
return *this;
}

~Polynom()
{
cout << "Start free k" << endl;
cout << "id = " << d << endl;
cout << "k = " << this->k << endl;
free(k);
cout << "End free k" << endl << endl;
}

int rank()
{
return n;
}

int operator[] (int index)
{
return k[index];
}

static Polynom sum(Polynom &a, Polynom &b)
{
int i;
int* k;
int n;
n = max(a.rank(), b.rank());
k = (int*)malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
k[i] = 0;
if (i < a.rank())
{
k[i] += a[i];
}
if (i < b.rank())
{
k[i] += b[i];
}
}
Polynom c(n, k);
free(k);
return Polynom(c.n, c.k);
//return c;
}

void Print()
{
int i;
for (i = 0; i < n; i++)
{
if (i)
{
printf("+ ");
}
printf("%d * a ^ %d ", k[n - i - 1], n - i - 1);
}
printf("\r\n");
}

private:
char d;
int* k;
int n;
};

int main()
{
c = 'a';
int k[10];
k[0] = 1;
k[1] = -2;
k[2] = 1;
Polynom a(3, k);
k[0] = -1;
k[1] = 1;
Polynom b(2, k);
//Polynom::sum(a, b);
//Polynom res(Polynom::sum(a, b));
Polynom res;
res = Polynom::sum(a, b);
res.Print();
return 0;
}
А ключевым моментом оказалось вот:
C++:
...
Polynom c(n, k);
free(k);
return Polynom(c.n, c.k);
//return c;
...
Оказывается, просто имеет очень большое значение, что мы возвращаем. Если возвращаем локальную переменную-объект, то всё рушится. А если возвращаем Polynom(параметры), то работает!!! ;)
Скажем так, проблему можно считать частично решённой. Частично - потому что понял, как поступать. А не понял, с чего вдруг какая-то есть разница в строчках:
C++:
		return Polynom(c.n, c.k);
//return c;
 
R

rrrFer

#6
Оказывается, просто имеет очень большое значение, что мы возвращаем. Если возвращаем локальную переменную-объект, то всё рушится. А если возвращаем Polynom(параметры), то работает!!!
Выкинь свой компилятор, я ж говорил.
Функция у тебя возвращает результат по значению, это значит, что будет вызван конструктор копирования. (в любом случае - не важно локальная это переменная {первый вариант} или временный объект {второй вариант}).
Ну еще, в зависимости от компилятора эти моменты могут как-то оптимизироваться - дело может быть в этом. Но

Ещё один update. Присваивания всё-таки работают:
Если такие присваивания, как у тебя, работают - то компилятор твой не работает.

В соседней теме ты про билдер спрашиваешь. Ты на нем и пишешь?
 
R

rrrFer

#7
Неужели между:

Polynom c(n, k);

и:

Polynom c;
c = Polynom(n, k);

есть разница, и ещё большая?
В первом случае вызывается конструктор с двумя аргументами. Больше ничего не вызывается.

Во втором, вызывается конструктор по умолчанию (без параметров), затем вызывается конструктор с двумя аргументами (и создается временный объект), затем, вызывается оператор присваивания.

На присваивании нормальный компилятор выдаст ошибку:
main.cpp:139:28: error: no match for ‘operator=’ in ‘res = Polynom::sum(Polynom&, Polynom&)((* & ;))’


main.cpp:139:28: note: candidate is:
main.cpp:45:14: note: Polynom& Polynom::eek:perator=(Polynom&)
main.cpp:45:14: note: no known conversion for argument 1 from ‘Polynom’ to ‘Polynom&’
Потому что аргумент оператор присваивания у тебя получает по ссылке, но ты передаешь туда временный объект (это значение) и следовательно, он не может преобразовать Polynom в Polynom&.

Почему нельзя просто взять и присвоить? Как же тогда работают всякие QString?
Мы же всегда может сделать, что-то типа:
QString str1;
QString str2;
QString str3;
str1 = "a";
str2 = "b";
str3 = str1 + str2;

Тут же ничего не вылетает. И присваивается объект в случае str3 = str1 + str2. Или я чего-то не понимаю?
Qt не так прост. Всегда можно посмотреть исходники:
inline QString(); //1
QString(QChar c); //2
QString &operator=(QChar c); // 3
QString &operator=(const QString &); // 4
#ifdef Q_COMPILER_RVALUE_REFS
inline QString(QString && other) : d(other.d) { other.d = Data::sharedNull(); } //5
inline QString &operator=(QString &&other) // 6
{ qSwap(d, other.d); return *this; }
#endif
inline const QString operator+(const QString &s1, const QString &s2) // 6
{ QString t(s1); t += s2; return t; }

Сначала будет 3 раза вызван конструктор по умолчанию (1), затем 2 раза конструктор QChar (3), затем оператор + (6), а после этого, как мне кажется, перемещающий конструктор (5).

МОжет быть я не прав.

Добавлено: вообще, твой код кривой.
Не должна функция sum возвращать объект по значению.
Возвращай либо ссылку, либо указатель.
 

Vadik(R)

Well-known member
12.12.2007
469
0
#8
В соседней теме ты про билдер спрашиваешь. Ты на нем и пишешь?
Не, в той теме я наоборот ответил (если Вы про эту), я даже и не заметил, что там Builder. Пишу на Visual Studio в основном. Есть ещё на ubuntu gcc, только там пока очень простенькие программы пишу.
Добавлено: вообще, твой код кривой.
Не должна функция sum возвращать объект по значению.
Возвращай либо ссылку, либо указатель.
В любом случае Вам спасибо ;) Есть какая-то моральная поддержка, что вообще есть ответы в теме :D Посмотрю в сторону других компиляторов

Добавлено
Ещё только что заметил ещё одну странность MS VS 2013:
C++:
.....
sum
....
Polynom *c = new Polynom(n, k);
free(k);
return *c;
То деструктор для *c всё равно вызывается, и утечек памяти не будет. Причём и код отработает без вылетов. Ну опять же, тогда свалим всё на причуды выжуалки
 
R

rrrFer

#9
Ну я не знаю...

Когда я учился в ВУЗе, нас ваще гоняли по BC3.1 (это такая древняя синяя хреновина, задуманная еще в начале 90х или конце 80х) - сейчас ее не дают вроде бы потому, что не запускается она на новых windows даже в режиме совместимости.

Дак вот там можно было делать так:

char *s;
scanf("%s", s);

Т.е. память под строки можно было не выделять. Это касалось именно строк, с другими массивами не канало. По легенде, ВС3.1 выделял как-то сам какой-то объем памяти (не известно какой) под строки ;). Я не знаю надо было освобождать эту память или нет. Сейчас я думаю, что может быть и не выделялось там ничего, просто по указателю выделялась память (он ведь тоже адрес имеет) и все это должно было приводить к крэшам (но мы их не наблюдали ни разу, мало того, в наших методичках примеры были такие же).

Опиши тут задачу (задание, которое решал), может быть я найду время и отвечу как бэ это могло выглядеть с моей точки зрения (она далека от идеала, но все же)
 

Vadik(R)

Well-known member
12.12.2007
469
0
#10
Задание в оригинале было так:
Составить описание класса многочленов от одной переменной, задаваемых сте­пенью многочлена и массивом коэффициентов. Предусмотреть методы для вы­числения значения многочлена для заданного аргумента, операции сложения, вычитания и умножения многочленов с получением нового объекта-многочлена, вывод на экран описания многочлена.

Написать программу, демонстрирующую работу с этим классом. Программа должна содержать меню, позволяющее осуществить проверку всех методов класса.
Выкладывая код, решил локализовать проблему, поэтому оставил только метод sum.