0

効率的な 2d ベクトルを実装する正しい方法は何ですか? Item オブジェクトのセットを 2 次元コレクションに格納する必要があります。これは、反復処理が速く (最も重要)、要素の検索も高速です。

次のように宣言されたポインターの 2d ベクトルがあります。

std::vector<std::vector<Item*>> * items;

コンストラクターでは、次のようにインスタンス化します。

items = new std::vector<std::vector<Item*>>();
items->resize(10, std::vector<Item*>(10, new Item()));

アイテムにアクセスするためのメソッドを (正しく) 実装するにはどうすればよいですか? 例えば:

items[3][4] = new Item();

AddItem(Item *& item, int x, int y)
{
     items[x][y] = item;
}

ポインターを使用する理由は、パフォーマンスを向上させるためです。これにより、参照によって物事を渡すことができます。

これについてもっと良い方法がある場合は、説明してください。ただし、ベクターを正しく使用する方法にはまだ興味があります。

編集: 明確にするために、これは単純なゲームでの在庫管理用のクラスの一部です。セット 10x10 ベクトルは、セット サイズである在庫グリッドを表します。Item クラスには、アイテム タイプ、リソース マネージャー内の画像へのポインター、スタック サイズなどが含まれます。

このクラスは反復され、画像ポインターを使用してフレームごとにインベントリ全体をレンダリングするために使用されるため、私のポインターの使用はパフォーマンスを改善するためのものでした。

4

3 に答える 3

3

行列の大きさは事前にわかっているようで、この行列は二乗されているようです。問題はありませvector<>んが、その場合はネイティブ ベクターを使用することもできます。

Item **m = new Item*[ n * n ];

位置 r,c にアクセスしたい場合は、r に n を掛けてから c を足すだけです:

pos = ( r * n ) + c;

したがって、位置 1、2、および n = 5 にアクセスする場合は、次のようになります。

pos = ( 1 * 5 ) + 2;
Item * it =  m[ pos ];

auto_ptrまた、単純なポインターを使用する代わりに、 (廃止) やなどのスマート ポインターを使用できますunique_ptr。これらは多かれ少なかれ似ています: それらが破棄されると、それらが指しているオブジェクトを破棄します。

auto_ptr<Item> m = new auto_ptr<Item>[ n * n ];

get()唯一の欠点は、ポインターを取得するために呼び出す必要があることです。

pos = ( 1 * 5 ) + 2;
Item * it =  m[ pos ].get();

ここに、これらすべてを要約したクラスがあります。

class ItemsSquaredMatrix {
public:
    ItemsSquaredMatrix(unsigned int i): size( i )
        { m = new std::auto_ptr<Item>[ size * size ]; }
    ~ItemsSquaredMatrix()
        { delete[] m; }

    Item * get(unsigned int row, unsigned int col)
        { return m[ translate( row, col ) ].get(); }
    const Item * get(unsigned int row, unsigned int col) const
        { return m[ translate( row, col ) ].get(); }

    void set(unsigned int row, unsigned int col, Item * it)
        { m[ translate( row, col ) ].reset( it ); }

    unsigned int translate(unsigned int row, unsigned int col) const
        { return ( ( row * size ) + col ); }

private:
    unsigned int size;
    std::auto_ptr<Item> * m;
};

あとはクラスを作成するだけですItem。ただし、特定のクラスを作成した場合は、ItemsSquaredMatrix新しいデータごとに複製する必要があります。C++ では、テンプレート内の上記のクラスの変換を含む、これに対する特定の解決策があります(ヒント:vector<>はテンプレートです)。あなたは初心者なのでItem、抽象クラスとして持つ方が簡単です。

class Item {
public:
    // more things...
    virtual std::string toString() const = 0;
};

そして、それらから作成するすべてのデータ クラスを派生させます。ただし、キャストを行うことを忘れないでください...

ご覧のとおり、多くの未解決の質問があり、物事を明らかにし続けるにつれて、より多くの質問が発生します。楽しみ!

お役に立てれば。

于 2012-11-05T18:40:59.400 に答える
2

数値計算の場合、データをメモリにできるだけローカルに保存する必要があります。たとえば、nbymマトリックスを作成している場合、次のように定義したくなるかもしれません。

vector<vector<double>> mat(n, vector<double>(m));

このアプローチには重大な欠点があります。まず、データがメモリ内で連続していることを期待するBLASやなどの適切な行列ライブラリでは機能しません。LAPACK第 2 に、たとえそうなったとしても、メモリ内で大量のランダム アクセスとポインタの間接化が発生し、行列演算のパフォーマンスが低下します。n*m代わりに、サイズが連続したメモリ項目のブロックが必要です。

vector<double> mat(n*m);

しかしvector、1 次元から 2 次元のインデックスに手動で変換する必要があるため、実際にはこれに a を使用する必要はありません。には、これを行うライブラリがいくつかありますC++。そのうちの 1 つがBlitz++ ですが、現在はあまり開発されていないようです。他の選択肢はArmadilloEigenです。詳細については、この以前の回答を参照してください。

たとえば、を使用するEigenと、行列宣言は次のようになります。

MatrixXd mat(n,m);

mat[i][j]として要素にアクセスしたり、 として行列を乗算したりできますmat1*mat2

于 2012-11-05T18:16:22.663 に答える
1

最初の質問は、なぜポインターなのかということです。へのポインタを使用する理由はほとんどありません。またstd::vector、ポインタのベクトルを使用することはそれほど頻繁ではありません。あなたの定義はおそらく次のようになります:

std::vector<std::vector<Item> > items;

、または少なくとも(たとえば、Itemがポリモーフィック階層のベースであると仮定して):

std::vector<std::vector<Item*> > items;

問題に関しては、最善の解決策は、asメンバーを含むある種のVector2Dクラスでデータをラップstd::vector<Item>し、インデックス計算を実行して目的の要素にアクセスすることです。

class Vector2D
{
    int my_rows;
    int my_columns;
    std::vector<Item> my_data;
public:
    Vector2D( int rows, int columns )
        : my_rows( rows )
        , my_columns( columns )
    {
    }

    Item& get( int row, int column )
    {
        assert( row >= 0 && row < my_rows
                && column >= 0 && column < my_columns );
        return my_data[row * my_columns + column];
    }

    class RowProxy
    {
        Vector2D* my_owner;
        int my_row;
    public;
        RowProxy(Vector2D& owner, int row)
            : my_owner( &owner )
            , my_row( row )
        {
        }
        Item& operator[]( int column ) const
        {
            return my_owner->get( my_row, column );
        }
    };
    RowProxy operator[]( int row )
    {
        return RowProxy( this, row );
    }

    //  OR...
    Item& operator()( int row, int column )
    {
        return get( row, column );
    }
};

境界チェックを忘れた場合(ただし、お勧めしません)、 RowProxy単純なものにすることができますItem*

そしてもちろん、上記を複製する必要がありますconst

于 2012-11-05T18:18:41.010 に答える