自动添加和删除列表中的对象

时间:2013-02-19 01:19:49

标签: c++ constructor shared-ptr weak-ptr

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

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

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

3 个答案:

答案 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}}堆栈中的对象。这是无效的。

对于您的使用案例,如果您希望deletetest更长,那么您可以只存储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;
    }
}