我上课了。实例化此类时,我希望将实例添加到列表中。删除对象后,我希望将其从列表中删除。
所以我给对象一个指向它自己的共享指针。然后我有一个指向这些共享指针的弱指针列表。创建对象时,它会创建一个指向自身的共享指针,指向该指针的弱指针,并将弱指针放在列表中。
当对象被销毁时,共享指针也是如此。每当我尝试访问列表中的成员时,我确保它没有过期并且其使用计数不为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)
答案 0 :(得分:3)
您的问题在于如何创建自我指针:
this->self = std::shared_ptr<test>(this);
使用此构造函数创建shared_ptr
时,根据documentation,
当T不是数组类型时,构造一个拥有指针p的shared_ptr。 ... p必须是指向通过C ++新表达式分配的对象的指针,或者是0
所以问题是shared_ptr
占用了你的堆栈对象,所以当对象被破坏时(和shared_ptr
一起),shared_ptr
正试图{ {1}}堆栈中的对象。这是无效的。
对于您的使用案例,如果您希望delete
比test
更长,那么您可以只存储vector
。
答案 1 :(得分:2)
我认为OP有兴趣解决他原来的问题,即使它使用的方法与他尝试的方法不同。下面是一个简单的示例,说明如何在构造对象时将对象添加到全局列表,并在删除对象时将其删除。要记住的一件事是:必须在添加到基类的每个构造函数中调用AddList。我不知道你是否希望列表可以在类外访问,所以我添加了getter函数来将非const迭代器返回到列表中。
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 :(得分:1)
这一行很麻烦:
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;
}
}