サイズ 800x800 の 2D int 配列を作成する必要があります。しかし、そうするとスタック オーバーフローが発生します (ははは)。
私は C++ が初めてなので、ベクトルのベクトルのようなことをする必要がありますか? そして、2次元配列をクラスにカプセル化するだけですか?
具体的には、この配列はグラフィックス プログラムの zbuffer です。画面上のすべてのピクセルの z 値を保存する必要があります (したがって、800x800 の大きなサイズになります)。
ありがとう!
約 2.5 メガが必要なので、ヒープを使用するだけで問題ありません。サイズを変更する必要がない限り、ベクターは必要ありません。「2D」ヒープ配列の使用例については、C++ FAQ Liteを参照してください。
int *array = new int[800*800];
delete[]
(終わったら忘れずに。)
これまでのすべての投稿は、プログラマーのメモリ管理に任せています。これは避けることができ、避けるべきです。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
ました。それを捕まえてくれたアンドレアスに感謝します!
ただし、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;
}
ベクトルのベクトルを実行できますが、オーバーヘッドが発生します。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];
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++ で優れた多次元配列を記述することについて、かなり良い議論があります。
static
インスタンスが 1 つだけ必要な場合は、(ファイルのスコープで、または関数スコープで修飾子を追加して) 静的ストレージに配列を割り当てることができます。
int array[800][800];
void fn()
{
static int array[800][800];
}
この方法では、スタックに移動せず、動的メモリを処理する必要がありません。
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
あなたができることの1つは、VCでスタックサイズを変更することです(実際にスタック上の配列が必要な場合)。これを行うためのフラグは [/F]( http://msdn.microsoft.com/en-us/library/ tdkhxaks(VS.80).aspx) .
しかし、おそらく必要な解決策は、メモリをスタックではなくヒープに置くことです。そのためには、vector
ofを使用する必要がありvectors
ます。
次の行は 800 個の要素を宣言していvector
ます。各要素はvector
800 個int
の であり、メモリを手動で管理する必要がありません。
std::vector<std::vector<int> > arr(800, std::vector<int>(800));
2 つの閉じ山括弧 ( ) の間のスペースに注意してください。これは、右シフト演算子とのあいまいさを解消するために必要です (これはC++0x では> >
不要になります)。
または、次のようなものを試すことができます。
boost::shared_array<int> zbuffer(new int[width*height]);
これもまだできるはずです:
++zbuffer[0];
メモリの管理について心配する必要はもうありません。カスタム クラスを処理する必要もなく、簡単に使い回せます。
まあ、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];
};