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によると、このコンストラクタで が作成されると、

T が配列型でない場合、ポインター p を所有する shared_ptr を構築します。... p は、C++ の new 式を介して割り当てられたオブジェクトへのポインターであるか、0 でなければなりません

したがって、問題は、shared_ptrがスタック オブジェクトの所有権を取得しているため、オブジェクトが破壊されたとき (およびそれshared_ptrに伴って)、スタック上にあるオブジェクトをshared_ptr試みてdeleteいることです。これは無効です。

あなたのユースケースでは、testが よりも長生きすると予想される場合はvector、 を保存するだけでよいかもしれませんthis

于 2013-02-19T01:29:28.510 に答える
2

OPは、彼が試みた方法とは異なる方法を使用していても、元の問題の解決策に関心があると思います. 以下は、オブジェクトを作成するときにグローバル リストにオブジェクトを追加し、削除するときにオブジェクトを削除する方法の簡単な例です。覚えておくべきことの 1 つは、基本クラスに追加するすべてのコンストラクターで 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 に答える