У меня есть класс. Когда этот класс создан, я хочу, чтобы экземпляр был добавлен в список. Когда объект удален, я хочу, чтобы он был удален из списка.
Поэтому я даю объекту общий указатель на себя. Затем у меня есть список слабых указателей на эти общие указатели. Когда объект создается, он создает общий указатель на себя, делает слабый указатель на него и помещает слабый указатель в список.
Когда объект уничтожен, разделяемый указатель также. Всякий раз, когда я пытаюсь получить доступ к члену в списке, я гарантирую, что срок его действия не истек и его счетчик использования не равен 0. Несмотря на это, я по-прежнему аварийно завершаю работу, когда член списка уничтожается. Зачем? Могу ли я обойти это? Вот мой SSCCE:
#include <iostream>
#include <memory>
#include <vector>
class test
{
private:
std::shared_ptr<test> self;
public:
int val;
test(int set);
test(test ©) = 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)
Ваша проблема в том, как вы создаете собственный указатель:
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
,
Я думаю, что ОП заинтересован в решении своей первоначальной проблемы, даже если он использует метод, отличный от того, который он пытался. Вот простой пример того, как добавить объект в глобальный список при его создании и удалить его при его удалении. Помните одну вещь: вы должны вызывать 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();
}
}
Эта линия является проблемой:
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;
}
}