Проблема с библиотекой LinkedList.h

TD27T
Offline
Зарегистрирован: 08.04.2016

Понадобились мне связанные списки. Гугл сходу предложил библиотеку LinkedList.h. Которой я и поспешил воспользоваться. Однако, убился о такую вот штуку:

#include<LinkedList.h>

class firstType{
};

class secondType{
  public:
    LinkedList<firstType*> list;
    
    secondType(){
      list = LinkedList<firstType*>();
    }
    secondType doSomething(){
      firstType* tmp=new firstType();
      list.add(tmp);
      return *this;
    }
    
};

void setup() {
  Serial.begin(9600);
  secondType* v=new secondType();
  v->doSomething();
  Serial.println(v->list.size());
  Serial.println(v->list.get(0)==NULL);
}

void loop() {}

На выходе получается 1 1, т.е. элементов в списке 1 и этот единственный элемент суть есть ничто.

Однако, если убрать строчку

return *this;

то вывод будет 1 0, т.е. список уже вполне адекватно себя чувствует.

Как такое может быть? Помогите ламеру, друзья, а то всю голову сломал уже...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А Вы понимаете, что делает return *this; ?

Попробуйте поставить отладочные печати во все методы и в конструктор (после строк 3, 10 и 13)  и понаблюдайте за поведением.

TD27T
Offline
Зарегистрирован: 08.04.2016

Я не уверен, что правильно понимаю, что делает return *this, вроде как пишут, что он должен клонировать объект. Проверить сейчас не могу, ибо на работе, но предположу, что конструкторы firstType и secondType должны вызваться по два раза, метод класса один раз. Не так?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Придёте домой - исследуете.

TD27T
Offline
Зарегистрирован: 08.04.2016

Это, конечно, да. Но если вы в курсе проблемы, то, может быть, в двух словах поясните, что происходит? Я просто совсем не понимаю, каким образом конструкция return *this убивает существующий экземпляр класса... Слишком долго сижу на C#, видимо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Знаете, Вот Вы разберётесь и будете знать прочно, на уровне понимания. Я любые мои объяснения дадут поверхностное "понимание на раз". Работайте сами.

TD27T
Offline
Зарегистрирован: 08.04.2016

Ваше право. Благодарю в любом случае.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

А Вы понимаете, что делает return *this; ?

То же стало интересно. Я вроде понимаю, что делает return *this;. Однако непонятно - doSomething() вызывается в void контексте и возвращаемое значение отбрасывается - почему это  меняет содержимое экземпляра v?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Поговорим, когда ТС поставит печати и расскажет, что получилось.

Кстати, ТС, для большей ясности, неплохо бы ещё поставить печать в конструкторе и деструкторе класса (перед строками 117 и 128). Думаю, так понятнее будет.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Я читаю и просто кожей чувствую, как у меня прокачивается скилл коррекционной педагогики.

Я остановлю этот праздник души и именины сердца. Просто из вредности... ;)

ТС! Строчка №13 ... хм... ну а какая же еще могла быть???

выглядит так:

secondType doSomething(){

а должна так:

secondType& doSomething(){

Понятна ли Вам разница, о мастер Си-Шарпа? ;) ;) ;)

-----------------------

Надеюсь на то, что педагогическую практику не испортил? ТС, как мне кажется, все равно не поймет, почему так происходит. Не поймет без подсказок Евгения.

-----------------------

Еще добавлю, что я издеваюсь по-доброму. Я сам не сильно люблю С++ за такие вот, неочевидные моменты.

Оно красиво, но не очевидно, что удобно. После моей подсказки, если знаете основы С++, то догадаетесь, если не догадались - не пользуйтесь такими конструкциями, там миллионы подводных камней.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula,

да тут до хрена всего, просто хочется, чтобы человек сам дошёл. Например, как соотносятся строки 8 и 11? Создаём экземпляр - выбрасываем нахрен, создаём новый, авось лучше будет! Я уж не говорю, про запрос памяти. Кстати, о памяти - её болезную под элемент списка сперва освобождаем, а потом кидаемся элемент печатать.

Хотя, зря всё это. Я хотел подсказать ТС про печати, а потом (в прорядке интерпретаии резальтатов) навести на литературу - самому доходить всегда полезнее.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Все сегодняшние примеры с непониманием у ТС - это мины, заложенные в С++ самим фактом неявных вызовов конструкторов и деструкторов.

Я не Архат, чтобы спорить с создателями языка ;), но ИМХО это не правильная "фича". Если мне нужно высокоуровневое программирование то есть Питон, или C#, для поклонников Винды. Си - язык низкоуровневого программирования. А С++ ушел далеко от Си - реально очень далеко,но не пришел к уровню Питона. Его место между Си и (условно)Питоном мне непонятно.

Ну да ладно... это не требует ответа - просто ворчание.

TD27T
Offline
Зарегистрирован: 08.04.2016

wdrakula пишет:

ТС! Строчка №13 ... хм... ну а какая же еще могла быть???

выглядит так:

secondType doSomething(){

а должна так:

secondType& doSomething(){

Понятна ли Вам разница, о мастер Си-Шарпа? ;) ;) ;)

Благодарю. Разница мне понятна, но не совсем. Т.е. я понимаю, что происходит в случае

secondType& doSomething(){return *this}

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

TD27T
Offline
Зарегистрирован: 08.04.2016

ЕвгенийП пишет:

 строки 8 и 11? Создаём экземпляр - выбрасываем нахрен, создаём новый, авось лучше будет!

Примерно так. Привык, что объявление переменной не означает её инициализацию. Этот вызов, конечно же избыточен.

ЕвгенийП пишет:

её болезную под элемент списка сперва освобождаем, а потом кидаемся элемент печатать

А вот это непонятно совсем. Как освобождаем память? Где?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

TD27T пишет:

А вот это непонятно совсем. Как освобождаем память? Где?

Вы, знаете, я вот совершенно искренне хочу Вам помочь, так не заставляйте меня выражаться матом! Я вам сказал поставить печати в Вашем скетче и в двух местах класса. Если Вы это сделаете, то увидите, что память освобождается ДО того, как Вы её печатаете. Тогда Вы поставите печати через строчку и увидите в какой именно строке она освобождается. Вот тогда Вы поймёте что не так. Причём реально поймёте и больше не будете повторять. Так сделайте это, чёрта возьми!

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

TD27T
Offline
Зарегистрирован: 08.04.2016

Буду дома - будет распечатка. Просто пока стараюсь докопаться до сути доступными мне средствами, а в данный момент в их число среда разработки на С++ не входит.

TD27T
Offline
Зарегистрирован: 08.04.2016

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

TD27T
Offline
Зарегистрирован: 08.04.2016

Итак, вот листинг с выводом трассы:

#include<LinkedList.h>

class firstType{
  public:
  firstType(){Serial.println("create first");}
  ~firstType(){Serial.println("delete first");}
   
};

class secondType{
  public:
    LinkedList<firstType*> list;
    
    secondType(){Serial.println("create second");}
    ~secondType(){Serial.println("delete second");}
    
    secondType doSomething(){
      Serial.println("enter doSomething");
      list.add(new firstType());
      Serial.println("exit doSomething");
      return *this;
    }
    
};

void setup() {
  Serial.println("enter setup");
  Serial.begin(9600);
  secondType v;
  v.doSomething();
  Serial.println(v.list.size());
  Serial.println(v.list.get(0)==NULL);
  Serial.println("exit setup");
}

void loop() {}

И вот что на выходе:

create second
enter doSomething
create first
exit doSomething
delete second
1
1
exit setup
delete second

Признаюсь, понятнее не стало...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, во-первых, Вы не поставили печать в конструкторе и деструкторе класса LinkedList, как я Вам советовал, а потому Вы не видите сколько экземляров класса Вы создаёте и когда их уничтожаете.

Найдите в библиотеке конструктор и деструктор и замените их хоть на такие

template<typename T>
LinkedList<T>::LinkedList()
{
	Serial.println( "LinkedList::LinkedList");

	root=NULL;
	last=NULL;
	_size=0;

	lastNodeGot = root;
	lastIndexGot = 0;
	isCached = false;
}

// Clear Nodes and free Memory
template<typename T>
LinkedList<T>::~LinkedList()
{
	Serial.println( "LinkedList::~LinkedList");
	ListNode<T>* tmp;
	while(root!=NULL)
	{
		tmp=root;
		root=root->next;
	Serial.print( "delete "); Serial.println((unsigned)tmp);
		delete tmp;
	}
	last = NULL;
	_size=0;
	isCached = false;
}

Кроме того, советую перед строкой 34 скетча поставить delete v; и увидеть, что память, которую Вы запрашиваете в строке 19, никогда и никем не освобождается. Но, это нам с Вами навырост, когда с первыми поблемами разберёмся.

TD27T
Offline
Зарегистрирован: 08.04.2016

Сделано

#include<LinkedList.h>

class firstType{
  public:
  firstType(){Serial.println("create first");}
  ~firstType(){Serial.println("delete first");}
   
};

class secondType{
  public:
    LinkedList<firstType*> list;
    
    secondType(){Serial.println("create second");}
    ~secondType(){Serial.println("delete second");}
    
    secondType doSomething(){
      Serial.println("enter doSomething");
      list.add(new firstType());
      Serial.println("exit doSomething");
      return *this;
    }
    
};

void setup() {
  Serial.begin(9600);
  Serial.println("enter setup");
  secondType v;
  v.doSomething();
  Serial.println("after doSomething");
  Serial.println(v.list.size());
  Serial.println(v.list.get(0)==NULL);
  delete &v;
  Serial.println("exit setup");
}

void loop() {}
inkedList<T>::LinkedList()
{
	Serial.println( "LinkedList::LinkedList");
	root=NULL;
	last=NULL;
	_size=0;

	lastNodeGot = root;
	lastIndexGot = 0;
	isCached = false;
}

// Clear Nodes and free Memory
template<typename T>
LinkedList<T>::~LinkedList()
{
	Serial.println( "LinkedList::~LinkedList");
	ListNode<T>* tmp;
	while(root!=NULL)
	{
		tmp=root;
		root=root->next;
		delete tmp;
	}
	last = NULL;
	_size=0;
	isCached = false;
}

Вывод:

enter setup
LinkedList::LinkedList
create second
enter doSomething
create first
exit doSomething
delete second
LinkedList::~LinkedList
after doSomething
1
1
delete second
LinkedList::~LinkedList
exit setup
delete second
LinkedList::~LinkedList
 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А почему Вы не вставили 25 строку из моего текста из предыдущего поста? Она как раз показывает когда класс освобождает элемент списка. Вставьте! Увидите, что память у Вас освобождается до Вашей попытки её читать.

А пока посчитайте сколько раз Вы создаёте экземпляр класса secondType  и сколько раз его освобождаете. Также посчитайте и для firstType.

Вас не смущает, что LinkedList Вы уничтожаете больше раз, чем создаёте? А firstType - наоборот, создаёте, а уничтожать дядя будет?

TD27T
Offline
Зарегистрирован: 08.04.2016

Виноват, не заметил.

Да, косяк с утечкой памяти я уже понял. Полагаю, delete &list в деструкторе secondType должно решить проблему?

Но мы ещё не приблизились к ответу на основной вопрос в теме: что происходит в sometype sometype::foo(){return *this;}

upd

Проверил, delete &list проблему не решает.

решает for(int i = 0;i<list.size();i++)delete list.get(i); создавать список указателей с этой библиотекой тупая мысль.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

TD27T пишет:

Полагаю, delete &list в деструкторе secondType должно решить проблему?

Нет. Хотя, попробуйте, чтобы убедиться самому. Кстати, странно, что Вы начали тут писать что именно Вы полагаете, не попробовав.

TD27T пишет:

Но мы ещё не приблизились к ответу на основной вопрос в теме: что происходит в sometype sometype::foo(){return *this;}

тут сложно, у Вас накручено одна ошибка на другой сидит. И Очень трудно отделить мух от котлет, т.к. они интерферируют.

Кроме того, Вы упорно игнорируете мою просьбу поставить печать в том месте, где элемент списка освобождается (возле delete) - строка 25 в моём посте #18

Сделайте это и скажите мне (глядя на свою выдачу) в какой именно момент происходит освобождение памяти элемента списка?

 

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Кому попкорн? ;)

Мне вмешаться со своим "французским"?  Я уже в нужной кондиции.... грамм 600 самогончика уже скушал... могу объяснить тонкости С++ на народном языке. Если что...

-----------------------

Посчитайте, есть ли живой линкед-лист к моменту печати. Как уничтожался ненужный экземпляр секонда из стека при ретурне? Последовательно, по шагам.

Сделайте это сами, а то ведь я могу не дождаться разрешения от Евгения ;) !

=============================

И внемите моему совету - это не простые моменты ООП. Не нужны они Вам совсем.

TD27T
Offline
Зарегистрирован: 08.04.2016

ЕвгенийП пишет:

Сделайте это и скажите мне (глядя на свою выдачу) в какой именно момент происходит освобождение памяти элемента списка?

Сделано. Вот скетч

#include<LinkedList.h>

class firstType{
  public:
  firstType(){Serial.println("create first");}
  ~firstType(){Serial.println("delete first");}
   
};

class secondType{
  public:
    LinkedList<firstType*> list;
    
    secondType(){Serial.println("create second");}
    ~secondType(){for(int i = 0;i<list.size();i++)delete list.get(i); Serial.println("delete second");}
    
    secondType doSomething(){
      Serial.println("enter doSomething");
      list.add(new firstType());
      Serial.println("exit doSomething");
      return *this;
    }
    
};

void setup() {
  Serial.begin(9600);
  Serial.println("enter setup");
  secondType v;
  v.doSomething();
  Serial.println("after doSomething");
  Serial.println(v.list.size());
  Serial.println(v.list.get(0)==NULL);
  delete &v;
  Serial.println("exit setup");
}

void loop() {}

и выдача

enter setup
LinkedList::LinkedList
create second
enter doSomething
create first
exit doSomething
delete first
delete second
LinkedList::~LinkedList
delete list item
after doSomething
1
1
delete second
LinkedList::~LinkedList
delete list item
exit setup
delete second
LinkedList::~LinkedList

TD27T
Offline
Зарегистрирован: 08.04.2016

wdrakula пишет:

Посчитайте, есть ли живой линкед-лист к моменту печати.

Нет

wdrakula пишет:

Как уничтожался ненужный экземпляр секонда из стека при ретурне?

Вот это для меня загадка.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

TD27T пишет:
Вот это для меня загадка.

ответ в вашей выдачи после exitDoSomething  и печатью. Дошло??????

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Женя, жду разрешения... обещаю почти не пользоваться матом.

TD27T
Offline
Зарегистрирован: 08.04.2016

wdrakula пишет:

TD27T пишет:
Вот это для меня загадка.

ответ в вашей выдачи после exitDoSomething  и печатью. Дошло??????

Хм... Как бы объяснить... Я понимаю, что он уничтожился. Я не понимаю почему.

Кстати, теперь я ещё не понимаю почему list.size() удалённого списка возвращает 1

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

это не простые моменты ООП

И к этой непростоте добавляется ещё и говнокод, гордо называемый библиотекой, который умудряется использовать класс ListNode без виртуального деструктора и без вызлва и, стало быть, без возможность освободить память элемента списка.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Блин, ну я с Вас балдею. У меня же в той печати стоял вывод адреса temp! нафига было удалять? Он бы нам пригодился.

Теперь видете, что list item удаляется до того, как Вы пытаетесь до него добраться и напечатать? Вилите или нет?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Женя, жду разрешения... обещаю почти не пользоваться матом.

Да, уж и я скоро начну. Четрые поста не можем вставить печать, которую я готовую дал.

TD27T
Offline
Зарегистрирован: 08.04.2016

ЕвгенийП пишет:

Блин, ну я с Вас балдею. У меня же в той печати стоял вывод адреса temp! нафига было удалять? Он бы нам пригодился.

Действительно, виноват. Но, если я правильно понял, то вся беда в том, что в деструкторе LinkedList вместо delete *firstClass вызывается delete **firstClass. Если это так, то давайте закроем тему с утечкой памяти. Если нет, то можете матом, я не обижусь.

ЕвгенийП пишет:

Теперь видете, что list item удаляется до того, как Вы пытаетесь до него добраться и напечатать? Вилите или нет?

Да, вижу. Удаляется. Удаляется вместе со списком и элементом secondClass, в котором он находится

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

TD27T пишет:

Действительно, виноват. Но, если я правильно понял, то вся беда в том, что в деструкторе LinkedList вместо delete *firstClass вызывается delete **firstClass. Если это так, то давайте закроем тему с утечкой памяти. Если нет, то можете матом, я не обижусь.

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

Но это другая проблема, давайте с Вашим *this разбираться.

Вы по прежденму не понимаете что происходит? Мне объяснять построчно?

TD27T
Offline
Зарегистрирован: 08.04.2016

ЕвгенийП пишет:

Но это другая проблема, давайте с Вашим *this разбираться.

Вы по прежденму не понимаете что происходит? Мне объяснять построчно?

Если вас не затруднит.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Вей' з мир!

========================

попробуйте посмотреть разницу в варианте, что давал давно со ссылкой и вашем, который использован в последней выдаче.

Сделайте такую же распечатку, только со ссылкой в описании функции ду-самсинг. Это была подсказка.

TD27T
Offline
Зарегистрирован: 08.04.2016

wdrakula пишет:

Вей' з мир!

========================

попробуйте посмотреть разницу в варианте, что давал давно со ссылкой и вашем, который использован в последней выдаче.

Сделайте такую же распечатку, только со ссылкой в описании функции ду-самсинг. Это была подсказка.

Уже пробовал, всё работает как надо, спасибо. А сейчас я хочу понять, какая магия происходит при варианте без ссылки.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

в стеке создается КОПИЯ нашего единственного секонд. КОПИЯ!!! Но данные то в ней такие же. И, при уничтожении копии, уничтожаются все элементы, а они ровно теже самые(!!!), что и в оригинале. Поэтому к-во элементов в распечатке - 1, а содержимое NULL. Теперь дошло? К-во элементов в ОРИГИНАЛЕ, никто не менял, поэтому оно осталось 1, а сами объекты - удалены.

==================

А при ссылке, как и положено, деструктор не вызывается.

----------------------

Еще раз - НЕ НАДО ВАМ ИСПОЛЬЗОВАТЬ ТАКИЕ КОНСТРУКЦИИ.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Тогда, смотрим на вот этот код. В него я вставил печать контрольных точек через строчку, чтобы было понятно, что и когда происходит. Я Вам писал про это Выше, но Вы не захотели их вставлять, а зря. Вот код

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

#define	INVESTIGATION_PRINT

#include <LinkedList.h>

class firstType {
	public:
	firstType(void) { Serial << "firstType::firstType\n"; }
	~firstType(void) { Serial << "firstType::~firstType\n"; }
};

class secondType {
  public:
    LinkedList<firstType*> list;

    secondType() {
	  Serial << "Point #2\n";
     list = LinkedList<firstType*>();
	  Serial << "Point #3\n";
    }
    secondType doSomething() {
	   Serial << "Point #5\n";
     firstType* tmp = new firstType();
  		Serial << "Point #6\n";
      Serial << "Got memory: " << ((unsigned)tmp) << '\n';
      list.add(tmp);
		Serial << "Point #7\n";
      return *this;
    }

};

void setup() {
  Serial.begin(115200);
  Serial << "Point #1\n";
  secondType* v = new secondType();
  Serial << "Point #4\n";
  v->doSomething();
  Serial << "Point #8\n";
  Serial << "v->list.size()=" << v->list.size() << '\n';
  Serial << "(v->list.get(0) == NULL) =" << (v->list.get(0) == NULL) << '\n';
  delete v;
  Serial << "Point #9\n";
}

void loop() {}

И вот, что он печатает

Point #1
LinkedList::LinkedList
Point #2
LinkedList::LinkedList
LinkedList::~LinkedList
Point #3
Point #4
Point #5
firstType::firstType
Point #6
Got memory: 737
LinkedList::add(737)
Point #7
LinkedList::~LinkedList
delete 741
Point #8
v->list.size()=1
(v->list.get(0) == NULL) =1
LinkedList::~LinkedList
delete 741
Point #9

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

Поехали.

После Point#1, при срабатывании строки 37 кода, ещё до того, как начнёт исполняться конструктор (Point#2) создаётся объект LinkedList (выдача - строка 2). Почему он создаётся? Потому что Вы описали его как свойство класса (именно его, а не указатель), а ствойства всегда иниализируются ДО вызова конструктора.

В строке 19 кода (между point№2 и  point№3) Вы создаёте новый экземпляр  LinkedList  (вызов конструктора в строке №4 выдачи) и присваиваете его свойтсву list. При этом ранее созданный экземпляр удаляется (вызов деструктора в строке №5 выдачи).

Дальше всё более или менее предсказуемо, до Point#7. Здесь Вы собираетесь возвращать *this

При этом происходит следующее.

1) создаётся новый объект, который собственно и будет возвращаться.
2) при этом в новый объект копируется содержимое текущего.

И вот тут самая тонкость. Даже две.

Для него не вызывается конструктор. Просто выделяется память и копируется содержиоме объекта. В прнципе, если бы у Вас был специальный "конструктор копирования", он был бы вызван, но его нет. Потому, создание копии происходит без вызова конструктора - простым копированим области памяти.

Вторая тонкость - текущий объет содержит указатели. Много указателей. И этти указатели копируется в новый объект. Но при этом не дублируется то, на что указатели указывают! Только копируются указатели. В результате, мы имеем два объекта (this и вновь созданный для возвращения значения) в которых есть одианковые указатели, указывающие на одни и те же области памяти! Это понятно?

Едем дальше.

Строка 14 выдачи показывает, что недавно созданный для возвращения объект удаляется (вызывается деструктор). Удаляется он потому. что создан был исключительно для возвращения значения функции, но этого значения никто не ждёт - оно никому нафиг не нужно.

При уделнии, он чистит свой список айтемов и прописывает везде нули.

Тут ещё одна тонкость. Деструктор прописывает нули по своим указателям. Но ведь указатели теперь у нас указывают на те же самые область памяти, на которые указывают указатели другого экземпляра! Так вышло при копировании! Т.е., освободив память элемента списка, мы обидели наш старый добрый объект - теперь его куазатель элемента списка указывает на освобожденную память, указатель "на первый" смотрит на 0 и т.п.

А вот size не поменялся, т.к. это просто переменная. Она была скопирована при копировании объекта, но она у каждого объекта своя - уникальная.

Так понятно?

Теперь "что делать?".

Или не использовать (явно или неявно) присваивание объектов ListItem друг другу или доопределить конструктор копирования (или на худой конец опероатор присваивания).

-----------------

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

TD27T
Offline
Зарегистрирован: 08.04.2016

ЕвгенийП пишет:

Строка 14 выдачи показывает, что недавно созданный для возвращения объект удаляется (вызывается деструктор). Удаляется он потому. что создан был исключительно для возвращения значения функции, но этого значения никто не ждёт - оно никому нафиг не нужно.

При уделнии, он чистит свой список айтемов и прописывает везде нули.

Евгений, большое вам спасибо. Этот момент я действительно упустил: что при удалении со стека копии вызовется деструктор для неё. Теперь всё понятно.

И вдогонку вопрос: не подскажите ли адекватную библиотеку работы со списками?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

Да это вполне адекватна, только не используйте указатели как элементы списка.

TD27T
Offline
Зарегистрирован: 08.04.2016

Кстати, вот ещё что подумал: мне кажется, что отсутствие вызова деструктора элемента в списке - это не баг, а вполне себе архитектурное решение. Ибо не всегда надо уничтожать элемент при удалении контейнера.

  int a = 99;
  LinkedList<int*> list1; list1.add(&a);
  LinkedList<int*> list2; list2.add(&a);
  delete &list1;

Как-то так.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Конечно не баг. Элемент вовсе не обязан быть указателем на экземпляр класса. Например, он имеет право быть числом, и какой ему нафиг деструктор. 

Класс NodeItem должен иметь виртуальный деструктор - это решит проблему. Надо, переопределяй и чисть, не надо - забей и ничего не делай.