10

サイズ 800x800 の 2D int 配列を作成する必要があります。しかし、そうするとスタック オーバーフローが発生します (ははは)。

私は C++ が初めてなので、ベクトルのベクトルのようなことをする必要がありますか? そして、2次元配列をクラスにカプセル化するだけですか?

具体的には、この配列はグラフィックス プログラムの zbuffer です。画面上のすべてのピクセルの z 値を保存する必要があります (したがって、800x800 の大きなサイズになります)。

ありがとう!

4

10 に答える 10

12

約 2.5 メガが必要なので、ヒープを使用するだけで問題ありません。サイズを変更する必要がない限り、ベクターは必要ありません。「2D」ヒープ配列の使用例については、C++ FAQ Liteを参照してください。

int *array = new int[800*800];

delete[](終わったら忘れずに。)

于 2008-09-14T22:13:48.443 に答える
10

これまでのすべての投稿は、プログラマーのメモリ管理に任せています。これは避けることができ、避けるべきです。ReaperUnreal は、配列ではなくベクトルを使用し、ディメンションのテンプレート パラメータを作成し、アクセス関数を変更することを除けば、私がやろうとしていることと非常によく似ています。

template <class T, size_t W, size_t H>
class Array2D
{
public:
    const int width = W;
    const int height = H;
    typedef typename T type;

    Array2D()
        : buffer(width*height)
    {
    }

    inline type& at(unsigned int x, unsigned int y)
    {
        return buffer[y*width + x];
    }

    inline const type& at(unsigned int x, unsigned int y) const
    {
        return buffer[y*width + x];
    }

private:
    std::vector<T> buffer;
};

これで、この 2 次元配列をスタックに適切に割り当てることができます。

void foo()
{
    Array2D<int, 800, 800> zbuffer;

    // Do something with zbuffer...
}

これが役立つことを願っています!

編集: から配列指定を削除しArray2D::bufferました。それを捕まえてくれたアンドレアスに感謝します!

于 2008-09-15T06:32:49.103 に答える
4

ただし、Kevin の例は良い例です。

std::vector<T> buffer[width * height];

する必要があります

std::vector<T> buffer;

もちろん、少し拡張すると、at() 関数の代わりに演算子のオーバーロードを追加できます。

const T &operator()(int x, int y) const
{
  return buffer[y * width + x];
}

T &operator()(int x, int y)
{
  return buffer[y * width + x];
}

例:

int main()
{
  Array2D<int, 800, 800> a;
  a(10, 10) = 50;
  std::cout << "A(10, 10)=" << a(10, 10) << std::endl;
  return 0;
}
于 2008-09-15T07:08:30.437 に答える
3

ベクトルのベクトルを実行できますが、オーバーヘッドが発生します。Z バッファーの場合、より一般的な方法は、サイズ 800*800=640000 の配列を作成することです。

const int width = 800;
const int height = 800;
unsigned int* z_buffer = new unsigned int[width*height];

次に、次のようにピクセルにアクセスします。

unsigned int z = z_buffer[y*width+x];
于 2008-09-14T22:14:57.940 に答える
2

800*800 の 1 次元配列を作成するかもしれません。800 個の個別のベクトルを割り当てるよりも、このように 1 つの割り当てを使用する方がおそらく効率的です。

int *ary=new int[800*800];

次に、おそらくそれを 2D 配列のように機能するクラスにカプセル化します。

class _2DArray
{
  public:
  int *operator[](const size_t &idx)
  {
    return &ary[idx*800];
  }
  const int *operator[](const size_t &idx) const
  {
    return &ary[idx*800];
  }
};

ここに示した抽象化には多くの穴があります。たとえば、「行」の末尾を超えてアクセスするとどうなるでしょうか。「Effective C++」という本には、C++ で優れた多次元配列を記述することについて、かなり良い議論があります。

于 2008-09-14T22:17:58.417 に答える
1

staticインスタンスが 1 つだけ必要な場合は、(ファイルのスコープで、または関数スコープで修飾子を追加して) 静的ストレージに配列を割り当てることができます。

int array[800][800];

void fn()
{
    static int array[800][800];
}

この方法では、スタックに移動せず、動的メモリを処理する必要がありません。

于 2008-09-15T13:01:38.747 に答える
1

Cのようなやり方があります:

const int xwidth = 800;
const int ywidth = 800;
int* array = (int*) new int[xwidth * ywidth];
// Check array is not NULL here and handle the allocation error if it is
// Then do stuff with the array, such as zero initialize it
for(int x = 0; x < xwidth; ++x)
{
    for(int y = 0; y < ywidth; ++y)
    {
         array[y * xwidth + x] = 0;
    }
}
// Just use array[y * xwidth + x] when you want to access your class.

// When you're done with it, free the memory you allocated with
delete[] array;

y * xwidth + x簡単な get および set メソッドを使用してクラス内にカプセル化できます([]より高度な C++ を使い始めたい場合は、演算子をオーバーロードすることもできます)。ただし、C++ を使い始めたばかりで、n 次元配列用の再利用可能な完全なクラス テンプレートの作成を開始していない場合は、ゆっくりとこれに取り組むことをお勧めします。開始時に混乱するだけです。

グラフィックスの作業に取り掛かるとすぐに、余分なクラス呼び出しによるオーバーヘッドがコードの速度を低下させる可能性があることに気付くかもしれません。ただし、アプリケーションが十分に高速ではなく、不必要な複雑さで最初に使用するのが難しくなるのではなく、どこで時間が失われたかを示すためにアプリケーションをプロファイリングできるようになるまで、これについて心配する必要はありません。

このような情報については、C++ lite FAQ が最適であることがわかりました。特に、あなたの質問は次のように答えられます:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16

于 2008-09-14T22:21:46.507 に答える
1

あなたができることの1つは、VCでスタックサイズを変更することです(実際にスタック上の配列が必要な場合)。これを行うためのフラグは [/F]( http://msdn.microsoft.com/en-us/library/ tdkhxaks(VS.80).aspx) .

しかし、おそらく必要な解決策は、メモリをスタックではなくヒープに置くことです。そのためには、vectorofを使用する必要がありvectorsます。

次の行は 800 個の要素を宣言していvectorます。各要素はvector800 個intの であり、メモリを手動で管理する必要がありません。

std::vector<std::vector<int> > arr(800, std::vector<int>(800));

2 つの閉じ山括弧 ( ) の間のスペースに注意してください。これは、右シフト演算子とのあいまいさを解消するために必要です (これはC++0x では> >不要になります)。

于 2008-09-15T06:53:40.317 に答える
1

または、次のようなものを試すことができます。

boost::shared_array<int> zbuffer(new int[width*height]);

これもまだできるはずです:

++zbuffer[0];

メモリの管理について心配する必要はもうありません。カスタム クラスを処理する必要もなく、簡単に使い回せます。

于 2008-09-15T07:37:57.730 に答える
-1

まあ、Niall Ryan が始めたことに基づいて構築すると、パフォーマンスが問題になる場合は、数学を最適化し、これをクラスにカプセル化することで、これをさらに一歩進めることができます。

それでは、ちょっとした数学から始めましょう。800 は 2 の累乗で次のように書けることを思い出してください。

800 = 512 + 256 + 32 = 2^5 + 2^8 + 2^9

したがって、アドレス指定関数を次のように記述できます。

int index = y << 9 + y << 8 + y << 5 + x;

したがって、すべてを適切なクラスにカプセル化すると、次のようになります。

class ZBuffer
{
public:
    const int width = 800;
    const int height = 800;

    ZBuffer()
    {
        for(unsigned int i = 0, *pBuff = zbuff; i < width * height; i++, pBuff++)
            *pBuff = 0;
    }

    inline unsigned int getZAt(unsigned int x, unsigned int y)
    {
        return *(zbuff + y << 9 + y << 8 + y << 5 + x);
    }

    inline unsigned int setZAt(unsigned int x, unsigned int y, unsigned int z)
    {
        *(zbuff + y << 9 + y << 8 + y << 5 + x) = z;
    }
private:
    unsigned int zbuff[width * height];
};
于 2008-09-14T22:29:03.430 に答える