Наследование
2638
26
Есть класс Base, от которого наследуются классы Child1, Child2, Child3. Пусть экземпляры дочерних классов описывают свойства айтемов какого-то меню, которые как-то выбираются пользователем.

Вопрос.
Что происходит с памятью в случае нижеприведенной реализации?

// ----------------------

void main(void)
{
Base *obj;

...

switch(int i)
{
case 1: obj = new Child1; break;
case 2: obj = new Child2; break;
case 3: obj = new Child3; break;
}
obj->show();
}

// ---------------------

Каким образом выделяется память под объекты, определяемые пользователем? Грубо говоря, если
передать в наш switch() последовательно 1, 2, 3, 2, 1, 2 или что-нить в этом роде, выделится память для всех объектов или же каждый раз объект будет переписываться в память начиная с позиции, на которую указвыает *obj?
Каждый раз натыкаясь на new программа будет создавать новый обьект указанного класса, переопределяя значение указателя obj. По-моему так.
Выделится память для всех объектов, но доступ будет только к последнему, а остальные будут болтаться в динамической памяти мусором. Самый простой выход - использовать массив указателей.
craxx
А ещё можно создать просто статическое меню. (не путать с заболеванием) :спок:
Электроник
Думаю, что если бы человека устраивало статическое, то он не парился бы с динамическим....:улыб:
если
передать в наш switch() последовательно 1, 2, 3, 2, 1, 2 или что-нить в этом роде, выделится память для всех объектов или же каждый раз объект будет переписываться в память начиная с позиции, на которую указвыает *obj?

Объекты будут каждый раз создаваться заново, а значение obj меняться. Чтобы не было утечек памяти (не Java все-таки), лучше написать что-то вроде:

void main(void)
{
Base *obj = NULL;

...

Child1 *child1 = NULL;
Child2 *child2 = NULL;
Child3 *child3 = NULL;

switch(i)
{
case 1:
if (!child1) child1 = new Child1;
obj = child1;
break;

case 2:
if (!child2) child2 = new Child2;
obj = child2;
break;

case 3:
if (!child3) child3 = new Child3;
obj = child3;
break;
}

if (obj) obj->show();

delete child1;
delete child2;
delete child3;
....

}

Тогда экземпляры menuitem'ов не будут дублироваться и не будут теряться ссылки на них.
Мертвая птица тебе поможет.
На сколько я помню, там достаточно хорошо описано что делает new и почему после него нужно делать delete, зачем нужны виртуальные диструкторы и т.д.
Well
Объекты будут каждый раз создаваться заново, а значение obj меняться. Чтобы не было утечек памяти (не Java все-таки), лучше написать что-то вроде:
......
Давно не трогал с++, помоему там проще сделать у наследников виртуальный диструктор и код тогда будет типа такого

void main(void)
{
Base *obj = NULL;

...


switch(i)
{
case 1:
obj = new child1;
break;

case 2:
obj = new child2;
break;

case 3:
obj = new child3;
break;
}

if (obj) obj->show();

delete obj;
....

}
Egor_M
помоему там еще у всех наследников должен быть виртуальный диструктор.
Ессно.
Well
поздно уже, не внимательно прочитал код.
исправил пост.

зачем там городить огороды с 3-мя указателями, если достаточно 1 -го указателя на Base и виртуальных деструкторов в потомках.
Egor_M
зачем там городить огороды с 3-мя указателями, если достаточно 1 -го указателя на Base и виртуальных деструкторов в потомках.
Ну, код там вообще сам по себе странный, задумка автора слишком не ясна, но можно, конечно, добавить всего одну строку после вызова ->show():

delete obj;

чтобы удалять использованный экземпляр объекта.
Та-ак.. вроде кое-что начинает проясняться.
Только вот виртуальный деструктор - это что?
Тот самый delete obj, выполняющийся по завершении работы функции?

А если у меня этих функций много? Более того, приложение виндовое, и в нем присутствует необходимость использования свойств объекта (одного из Child'ов) при нажатии на кнопку.
Грубо говоря, нажал одну кнопку - пускается одна функция объекта, вторую - другая.

То есть delete памяти под объект представляется возможным сделать только в момент выделения памяти под другой (при выборе очередного айтема пользователем).

Разумно ли нижеследующее?

void main(void)
{
Base *obj = NULL;

...

switch(int i)
{
delete obj; // пользуемся тем, что указатель остается
case 1: obj = new Child1; break;
case 2: obj = new Child2; break;
case 3: obj = new Child3; break;
}

obj->show();
}

А мертвая птица - она что-то пока мне кажется пособием для каких-то иноземных существ.
delete obj; // пользуемся тем, что указатель остается
case 1: obj = new Child1; break;
case 2: obj = new Child2; break;
case 3: obj = new Child3; break;
Неа не правильно...
Как же указатель у тебя остался, если ты в начале функции присвоил его равным NULL? Да и потом obj - это локальная переменная.
Еще раз повторю: используй массив указателей, это проще всего.
craxx
А как это будет выглядеть?
Указатель для каждого из Child'ов?
Ну примерно так:
void main(void)
{
int i;
Base *obj[3] ={ NULL,NULL,NULL);

...

switch(i)
{
case 1: obj[i] = new Child1; break;
case 2: obj[i] = new Child2; break;
case 3: obj[i] = new Child3; break;
}
//какие-то действия
obj[i]->show();
delete obj[i];
}
Или что-то похожее...
craxx
1. В предложенном Вами варианте придется делать нечто вроде obj[0]->show();
Узнать, каким вышел объект из свитча можно только, таская за ним какое-то int'овое значение значение (определяемое, кстати, в том же свитче) для использования в виде
obj[ i ]->show();
Но проблема в том, что я могу делать delete только на этапе выделения памяти для другого объекта.

2. Такой еще вопрос.
В чем разница между
Base *obj[3] ={ NULL,NULL,NULL };
и
Base *obj[3];

Идентичны ли эти определения? Ведь в каждом из случаев создается массив указателей, указывающих ни на что.
Определения не идентичны, во втором случае может быть любое значение, т.к. просто резервируется память в стеке.
В принципе массив можно стделать динамическим или вобще вектором, а память освобождать в кнце программы.
А мертвая птица - она что-то пока мне кажется пособием для каких-то иноземных существ.
Книга такая,
Страуструп, Язык С++ , я читал 3-е издание, там все было достаточно понятно описано.

Диструктор - функция такая, которая вызывается при уничтожении объекта.

Если диструктор не виртуальный то для того, что бы код
Base *a=new Child1 ;
delete a;
отрабатывал коректно, т.е. уничтожел в памяти именно объект класса Child1 а не объект класса Base для каждого наследника нужно переопределять оператор delete, что выглядит убого, проще создать пусть даже пустой виртуальный диструктор.


Блин, ты бы почитал литературу хоть немного, нафига сразу в форум переть с такими вопросами.

Ответы нв вопросы: что такое диструктор? что такое виртуальная функция? и подобные ты найдешь практически в любой книге по С++.
craxx
Еще раз повторю: используй массив указателей, это проще всего.
Просто напомнило мне рассказ моего друга как он сдавал программирование на паскале в универе.
Тема была указатели, когда он сдавал прогу , препод(женщина) спросила указывая на какую-то переменную, что это такое. Он ответил, что это массив указателей.
После этого ответа она была в замешательстве секунд 30, и потом сразу поставила ему 5.
Видимо ей самой тяжело далось понятие указателя, а их массив просто повер ее в шок.
И все-таки действительно ли так необходимо динамическое меню. Ведь пункт меню должен создаваться (дополняться ) один раз и, соответственно ВСЕ динамическое меню должно быть delete перед завершением программы . Способ активно/неактивно отображаемо/неотображаемо неприемлем?
Egor_M
Помню как-то я сдавал программирование в Универе, так я зафигачил двусвязный список матриц на Паскале....:улыб:
Препод тоже была женщина. И тоже поставила 5, хотя теорию я ей завалил на откровенную пару :ха-ха!:
Электроник
И все-таки действительно ли так необходимо динамическое меню. Ведь пункт меню должен создаваться (дополняться ) один раз и, соответственно ВСЕ динамическое меню должно быть delete перед завершением программы . Способ активно/неактивно отображаемо/неотображаемо неприемлем?
Неа. Просто много пунктов меню, и в таком случае уж слишком много памяти используется.
Egor_M
Блин, ты бы почитал литературу хоть немного, нафига сразу в форум переть с такими вопросами.

Ответы нв вопросы: что такое диструктор? что такое виртуальная функция? и подобные ты найдешь практически в любой книге по С++.
Я вполне знаком с упомянутыми понятиями, просто они не в достаточной мере мной "прощупаны" на практике до уровня решения вот, например, такой задачи, какая приведена здесь.

Несовершенность своих знаний мне очевидна не меньше Вашего, и пришел я не за готовым кодом, а за советом.

Не все рождаются гуру, опыт приходит со временем.

Удаляюсь на очередное чтение литературы, без этого действительно никуда.
Я вполне знаком с упомянутыми понятиями, просто они не в достаточной мере мной "прощупаны" на практике до уровня решения вот, например, такой задачи, какая приведена здесь.
Зря ты пропустил главы о динамическом выделении памяти. А если и пропустил, при решении ЛЮБОЙ задачи правильный подход сначала почитать про это в книгах, почитать документацию, потом уже лезть в форумы с вопросами, если что-то из прочитаного не понятно или ты поискав не нашел ответа на свой вопрос...

Вот из MSDN:


When new is used to allocate memory for a C++ class object, the object's constructor is called after the memory is allocated.

Use the delete operator to deallocate the memory allocated with the new operator.
Egor_M
>Диструктор - функция такая, которая вызывается
>при уничтожении объекта.

Никаких "диструкторов" у трупа страуса нет ;-))
Разве не видите, что с этим примером что-то не так. Проблема возникла от того, что в нормальном коде такого быть не должно.
Никаких "диструкторов" у трупа страуса нет ;-))
Хорошо, по русскому мне 2:)

Но "деструкторов" там дофига....