Автоматическое добавление и удаление объекта из списка

У меня есть класс. Когда этот класс создан, я хочу, чтобы экземпляр был добавлен в список. Когда объект удален, я хочу, чтобы он был удален из списка.

Поэтому я даю объекту общий указатель на себя. Затем у меня есть список слабых указателей на эти общие указатели. Когда объект создается, он создает общий указатель на себя, делает слабый указатель на него и помещает слабый указатель в список.

Когда объект уничтожен, разделяемый указатель также. Всякий раз, когда я пытаюсь получить доступ к члену в списке, я гарантирую, что срок его действия не истек и его счетчик использования не равен 0. Несмотря на это, я по-прежнему аварийно завершаю работу, когда член списка уничтожается. Зачем? Могу ли я обойти это? Вот мой SSCCE:

#include <iostream>
#include <memory>
#include <vector>

class test
{
private:
std::shared_ptr<test> self;

public:
int val;
test(int set);

test(test &copy) = delete; // making sure there weren't issues
// with a wrong instance being deleted
};

std::vector<std::weak_ptr<test>> tests;

test::test(int set):
val(set)
{
this->self = std::shared_ptr<test>(this);

tests.push_back(std::weak_ptr<test>(this->self));
}

void printTests()
{
for (auto i = tests.begin(); i != tests.end(); i++)
{
if (i->use_count() == 0 || i->expired())
{
tests.erase(i);
continue;
}

std::cout << i->lock()->val << std::endl;
}

std::cout << std::endl;
}

int main(int argc, char **argv)
{
{
test t(3);

std::cout << "First tests printing: " << std::endl;

printTests();
} // SEGFAULTS HERE

std::cout << "Second tests printing: " << std::endl;
printTests();

return 0;
}

Вывод этой программы следующий:

First tests printing:
3

Segmentation fault (core dumped)

0

Решение

Ваша проблема в том, как вы создаете собственный указатель:

 this->self = std::shared_ptr<test>(this);

Когда shared_ptr создается с помощью этого конструктора, в соответствии с документация,

Когда T не является типом массива, создает shared_ptr, которому принадлежит указатель p.

p должен быть указателем на объект, который был выделен с помощью нового выражения C ++, или быть 0

Так что проблема в том, что shared_ptr становится владельцем вашего стекового объекта, поэтому, когда объект разрушается (и shared_ptr наряду с этим), shared_ptr пытается delete ваш объект, который находится в стеке. Это недействительно.

Для вашего случая использования, если вы ожидаете testчтобы пережить ваш vectorтогда вы можете просто хранить this,

3

Другие решения

Я думаю, что ОП заинтересован в решении своей первоначальной проблемы, даже если он использует метод, отличный от того, который он пытался. Вот простой пример того, как добавить объект в глобальный список при его создании и удалить его при его удалении. Помните одну вещь: вы должны вызывать AddList в каждом конструкторе, который вы добавляете в базовый класс. Я не знал, хотите ли вы, чтобы список был доступен вне класса или нет, поэтому я добавил функции получения, чтобы возвращать неконстантные итераторы в список.

class MyClass
{
private:
static std::list<MyClass*> mylist;
std::list<MyClass*>::iterator mylink;

// disable copy constructor and assignment operator
MyClass(const MyClass& other);
MyClass& operator = (const MyClass& other);

void AddList()
{
mylink = mylist.insert(mylist.end(), this);
}

void RemoveList()
{
mylist.erase(mylink);
}

public:
MyClass()
{
AddList();
}

virtual ~MyClass()
{
RemoveList();
}

static std::list<MyClass*>::iterator GetAllObjects_Begin()
{
return mylist.begin();
}

static std::list<MyClass*>::iterator GetAllObjects_End()
{
return mylist.end();
}

virtual std::string ToString() const
{
return "MyClass";
}
};

class Derived : public MyClass
{
virtual std::string ToString() const
{
return "Derived";
}
};

std::list<MyClass*> MyClass::mylist;int main()
{
std::vector<MyClass*> objects;
objects.push_back(new MyClass);
objects.push_back(new MyClass);
objects.push_back(new Derived);
objects.push_back(new MyClass);

for (std::list<MyClass*>::const_iterator it = MyClass::GetAllObjects_Begin(), end_it = MyClass::GetAllObjects_End(); it != end_it; ++it)
{
const MyClass& obj = **it;
std::cout << obj.ToString() << "\n";
}

while (! objects.empty())
{
delete objects.back();
objects.pop_back();
}
}
2

Эта линия является проблемой:

tests.erase(i);

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

auto i = tests.begin();
while (i != tests.end())
{
if (i->use_count() == 0 || i->expired())
{
i = tests.erase(i);
}
else {
std::cout << i->lock()->val << std::endl;
++i;
}
}
1
По вопросам рекламы [email protected]