D>class A {};
D>class B : public A
D>{
D> int i[1000];
D>};
D>int main()
D>{
D> A *a = new B();
D> delete a; // будет ли здесь утечка?
D> return 0;
D>}
D>
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000.
Код содержит неопределенное поведение. Дли исправления ситуации деструктор A нужно сделать виртуальным.
D>Наверное, я что-то упустил при изучении наследования.
Да, похоже на то
D>valgrind не видит утечки.
Одно из проявлений неопределенного поведения
D>class A {};
D>class B : public A
D>{
D> int i[1000];
D>};
D>int main()
D>{
D> A *a = new B();
D> delete a; // будет ли здесь утечка?
D> return 0;
D>}
D>
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000. D>Наверное, я что-то упустил при изучении наследования. D>valgrind не видит утечки.
А вы вот так попробуйте:
class A {};
class B : public A
{
public:
B()
: s_(new char[10])
{}
~B()
{
delete [] s_;
}
private:
char * s_;
}
A * a = new B();
delete a;
И>у класса A должен быть виртуальный деструктор.
B::~B() сделает смещение в памяти на 1000 байт?
Не знаю, где искать в стандарте языка. В нем это есть?
Мне кажется:
delete a; потом free(a); a->~B();
free() освободит sizeof(*a) и 1000 байт останутся как используемые.
Деструктор же это просто функция и понятия об атрибутах класса не имеет.
Я прав?
Здравствуйте, dronord, Вы писали:
И>>у класса A должен быть виртуальный деструктор. D>B::~B() сделает смещение в памяти на 1000 байт? D>Не знаю, где искать в стандарте языка. В нем это есть?
в стандарте языка это есть.
в общем, здесь два пути рарзешения проблемы:
1. рекомендованный. у базового класса должен быть виртуальный деструктор. тогда оператор delete вызовет деструктор конкретного типа, а не того, который ему подсунули.
2. сделать явное приведение типов, например
Здравствуйте, dronord, Вы писали:
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000. D>Наверное, я что-то упустил при изучении наследования. D>valgrind не видит утечки.
Вообще-то в стандарте это опреелено, как UB, так что может быть что угодно.
Но в реальных системаз аллокаторы обычно устроены так, что получают на вход указатель на начало блока, а рамер блока находят как-то сами (кури описание функции operator delete( void * ) ).
Это, например, аналогично C-шной традиции, когда писали alloc и free. При этов во free hfpvth блока не передавали.
Так что на большинстве реальных систем утечки не будет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, alexeiz, Вы писали:
A>А вы вот так попробуйте: A>
A>class A {};
A>class B : public A
A>{
A>public:
A> B()
A> : s_(new char[10])
A> {}
A> ~B()
A> {
A> delete [] s_;
A> }
A>private:
A> char * s_;
A>}
A>A * a = new B();
A>delete a;
A>
A>С вас $30
Если я не ошибаюсь то с вас надо брать в несколько раз больше.
Так как виртуального деструктора у класса A так и не появилось
A>
A>class A {
public:
virtual ~A(){}
};
class B : public A
{
int i[1000];
};
int main()
{
A *a = new B();
delete a; // будет ли здесь утечка?return 0;
}
D>class A {};
D>class B : public A
D>{
D> int i[1000];
D>};
D>int main()
D>{
D> A *a = new B();
D> delete a; // будет ли здесь утечка?
D> return 0;
D>}
D>
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000. D>Наверное, я что-то упустил при изучении наследования. D>valgrind не видит утечки.
Я думаю примерно так: "delete a" разбивается на 2 части:
— вызов деструктора ~A (действительно невиртуального)
— вызов operator delete (который собственно и освободит память)
По первой части: течь нечему, всё тривиально (хотя в более сложном случае может и потечь)
По второй части: будет освобождена вся выделенная память размером sizeof (B)
dronord wrote:
> class A {}; > class B : public A > { > int i[1000]; > }; > > int main() > { > A *a = new B(); > delete a; // будет ли здесь утечка? > return 0; > }
> Наверное, я что-то упустил при изучении наследования. > valgrind не видит утечки.
D>class A {};
D>class B : public A
D>{
D> int i[1000];
D>};
D>int main()
D>{
D> A *a = new B();
D> delete a; // будет ли здесь утечка?
D> return 0;
D>}
D>
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000. D>Наверное, я что-то упустил при изучении наследования. D>valgrind не видит утечки.
Всего лишь не вызовется деструктор B. Утечки таки не будет.
int main()
{
void *a = new B();
delete a; // и сколько, по-вашему, здесь байт удаляется? :) return 0;
}
Здравствуйте, игппук, Вы писали:
И>не надо с него больше брать. человек намеренно привел немного расширенный код автора, чтобы показать, в как получить конкретную утечку.
Я просто в шоке — этот вопрос обсуждается практически каждый месяц, и все равно всегда находятся люди, гнотовые спорить по этому поводу
5.3.5/3
In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual
destructor or the behavior is undefined.
B>Я просто в шоке — этот вопрос обсуждается практически каждый месяц, и все равно всегда находятся люди, гнотовые спорить по этому поводу
B>
B>5.3.5/3
B>In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual
B>destructor or the behavior is undefined.
Спасибо за стандарт! Также нашел в книге Б.Страуструп 3е изд., стр.477, "15.6 Свободная память"
Скажите, а массив вообще не удастся удалить?
In the second alternative (delete array) if the dynamic type of the
object to be deleted differs from its static type, the behavior is undefined.
Здравствуйте, dronord, Вы писали:
D>Скажите, а массив вообще не удастся удалить?
Ну, если тебе плевать на UB, и деструкторы тривиальные, то удастся конечно, на большинстве реализаций...
В этом смысле ситуация такая же, как с уалением одного объекта, без виртуального деструктора.
Но с массивом есть большая беда. Кроме того, что его не удастся удалить, с ним вообе ничего сделать не удастся, на самом деле
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, dronord, Вы писали:
D>>Скажите, а массив вообще не удастся удалить?
E>Ну, если тебе плевать на UB, и деструкторы тривиальные, то удастся конечно, на большинстве реализаций... E>В этом смысле ситуация такая же, как с уалением одного объекта, без виртуального деструктора.
Можешь привести пример такой реализации?
Я до сих пор встречал только один случай, где массив наследников корректно удалялся через указатель на базу (здесь
D>class A {};
D>class B : public A
D>{
D> int i[1000];
D>};
D>int main()
D>{
D> A *a = new B();
D> delete a; // будет ли здесь утечка?
D> return 0;
D>}
D>
D>Как я понимаю, удаляется sizeof(*a). Это 1, а не 1000. D>Наверное, я что-то упустил при изучении наследования. D>valgrind не видит утечки.
Здесь 2 альтернативы —
1. Сделать виртуальный деструктор у класса А.
2. Сделать деструктор класса А скрытым. Тогда код вообще перестанет компилироваться.
То, что вы привели — это неопределённое поведение. Худший случай — если у вас не будет утечек памяти и всё будет работать нормально. А потом при смене\обновлении компилятора или изменении каких-либо условий вы получите неожиданный сюрприз.