0

我有一堂课。实例化此类时,我希望将实例添加到列表中。删除对象后,我希望将其从列表中删除。

所以我给对象一个指向它自己的共享指针。然后我有一个指向这些共享指针的弱指针列表。当一个对象被创建时,它会创建一个指向自身的共享指针,创建一个指向它的弱指针,并将弱指针放入一个列表中。

当对象被销毁时,共享指针也被销毁。每当我尝试访问列表中的成员时,我都会确保它没有过期并且它的使用计数不为 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)
4

3 回答 3

3

您的问题在于如何创建自指针:

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

shared_ptr根据文档,使用此构造函数创建a 时,

当 T 不是数组类型时,构造一个拥有指针 p 的 shared_ptr。... p 必须是指向通过 C++ new 表达式分配的对象的指针,或者为 0

所以问题是shared_ptr你的堆栈对象的所有权,所以当对象被破坏(以及shared_ptr随之而来的)时,shared_ptr正在尝试delete你在堆栈上的对象。这是无效的。

对于您的用例,如果您希望tests 比您的 s 寿命更长vector,那么您可以只存储this.

于 2013-02-19T01:29:28.510 回答
2

我认为 OP 对解决他最初的问题很感兴趣,即使它使用的方法与他尝试的方法不同。下面是一个简单的例子,说明如何在构造对象时将其添加到全局列表中,并在删除时将其删除。要记住的一件事:您必须在添加到基类的每个构造函数中调用 AddList。我不知道您是否希望列表可以在类外访问,所以我添加了 getter 函数来将非常量迭代器返回到列表中。

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();
    }
}
于 2013-02-19T13:04:08.483 回答
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;
    }
}
于 2013-02-19T01:29:03.333 回答