1

私は自分のより大きなプログラムの問題を実証するためにテストケースプログラムを書いてきましたが、テストケースには元のプログラムにはないバグがあります。

ヘッダーファイルは次のとおりです。

// compiled with g++ -I/usr/local/bin/boost_1_43_0 -Wall -std=c++0x -g test.cpp

#include <bitset>
#include <boost/shared_ptr.hpp>
#include <vector>

typedef std::vector< std::vector< std::bitset<11> > > FlagsVector;

namespace yarl
{
    namespace path
    {
        class Pathfinder;
    }

    namespace level
    {
        class LevelMap
        {
        // Member Variables
            private:
                int width, height;
                FlagsVector flags;

            public:
                boost::shared_ptr<path::Pathfinder> pathfinder;

        // Member Functions
            LevelMap(const int, const int);

            int getWidth() const {return width;}
            int getHeight() const {return height;}

            bool getFifthBit(const int x, const int y) const
            {
                return flags.at(x).at(y).test(5);
            }
        };



        class Level
        {
        // Member Variables
            public:
                LevelMap map;

        // Member Functions
            public:
                Level(const int w=50, const int h=50);
        };
    }


    namespace path
    {
        class Pathfinder
        {
        // Member Variables
            private:
                boost::shared_ptr<level::LevelMap> clientMap;

        // Member Functions
            public:
                Pathfinder() {}
                Pathfinder(level::LevelMap* cm)
                : clientMap(cm) {}

                void test() const;
        };
    }
}

実装ファイルは次のとおりです。

#include <iostream>
#include "test.hpp"
using namespace std;

namespace yarl
{
    namespace level
    {
        LevelMap::LevelMap(const int w, const int h)
        : width(w), height(h), flags(w, vector< bitset<11> >(h, bitset<11>())),
          pathfinder(new path::Pathfinder(this)) 
        {}



        Level::Level(const int w, const int h)
        : map(w,h)
        {
            map.pathfinder->test();
        }
    }



    namespace path
    {
        void Pathfinder::test() const
        {
            int width = clientMap->getWidth();
            int height = clientMap->getHeight();
            cerr << endl;
            cerr << "clientMap->width: " << width << endl; 
            cerr << "clientMap->height: " << height << endl; 
            cerr << endl;
            for(int x=0; x<width; ++x)
            {
                for(int y=0; y<height; ++y)
                {
                    cerr << clientMap->getFifthBit(x,y);
                }
                cerr << "***" << endl; // marker for the end of a line in the output
            }
        }
    }
}

int main()
{
    yarl::level::Level l;
    l.map.pathfinder->test();
}

このプログラムを電気柵にリンクすると、実行すると次のエラーで中止されます。

ElectricFence Aborting: free(bffff434): address not from malloc().

Program received signal SIGILL, Illegal instruction.
0x0012d422 in __kernel_vsyscall ()

gdbからのバックトレースは、不正な命令がコンパイラによって生成されたのデストラクタにあることを示していPathfinderます。これは、shared_ptrの破棄に問題があります。誰もがそれがなぜであるかわかりますか?

4

3 に答える 3

4
yarl::level::Level l;

自動Level変数をインスタンス化します。これは、コンストラクターでpathfinder次のようにメンバーを作成します。

pathfinder(new path::Pathfinder(this))

次に、Pathfinderコンストラクターで、Level渡したポインターを受け取り、それをに割り当てますshared_ptr。次にshared_ptr、このポインタの所有権を取得します。

これはいくつかの理由で正しくありません。

  1. Ashared_ptrは、自動的に割り当てられたオブジェクトではなく、動的に割り当てられたオブジェクトを管理するために使用する必要があります
  2. を使用したい場合はshared_ptr、どこでも使用する必要があります。現在のように、生のポインタを渡します(たとえば、のコンストラクタに渡しますが、sPathfinderとして保存しますshared_ptr。これにより、所有権ワームの大きな缶が開きます。
  3. thisaに割り当てる正しい方法は、 ;shared_ptrから派生することです。enable_shared_from_thisただし、コンストラクターでshared_ptrfromを取得できないことに注意してください。this

shared_ptr破棄されると、管理しているポインタを削除しようとします。ただし、この場合、そのポインタは動的に割り当てられたオブジェクト(つまり、で割り当てられたnew)ではなく、自動的に割り当てられたオブジェクト(つまり、スタック上)を指します。したがって、エラー。

リソースの所有権を取得するために何かが必要ない場合は、rawポインター(または、そのオプションがある場合は参照)を使用しても問題はありません。

于 2010-07-25T19:20:08.390 に答える
2

shared_ptrshared_ptrによって管理されることを想定していないポインターから構築しています。(thisポインタ)

最後のshared_ptrのコピーが破棄されると、このメモリは解放されます(実際には解放されるべきではありませんthisが)。この場合、はスタック上にあります。

のコンストラクターが明示的である理由がありshared_ptrます-shared_ptrによって管理されない通常のポインターからshared_ptrへのそのような見過ごされていない変換を避けるためです-そのようなポインターをshared_ptrに渡すと、プログラムは運命づけられます-唯一の解決策は、削除するつもりのないポインタを削除することです。

一般に、newを使用して共有ポインタを直接構築することをお勧めします(たとえば、ptr(new Somethings(x,y,z)この方法では、割り当てられているが、shared_ptrメモリに割り当てられていない例外がリークするリスクはありません。

于 2010-07-25T19:20:23.730 に答える
1

にはメンバー変数Levelが含まれます。LevelMapLevel破壊されると、それも破壊されますLevelMap

一方、このLevelMapメンバーへのポインターはに渡されPathfinder、渡されたポインターからを作成shared_ptr<>します。この新しく作成されshared_ptr<>たものは、それが指すオブジェクトを所有していると考え、破壊されるとそれを破壊しようとしPathfinderます。

したがって、LevelMapは数回破壊されます。

この例でLevelMapは、はスタック上に作成されます。したがって、delete呼び出されたshared_ptr<>ユーザーは、アドレスがヒープからのものではないことを確認でき、エラーが発生します。実際のプログラムにもこの問題があるが、それらすべてのオブジェクトが動的に割り当てられている場合、エラーはおそらく検出されません。後でサイレントメモリの破損と奇妙なクラッシュが発生します。

于 2010-07-25T19:26:05.557 に答える