如何在C ++中解决非常大的2D数组
我需要创建一个大小为800x800的2D int数组。但是这样做会导致堆栈溢出(哈哈)。
我是C ++的新手,所以我应该做一些像vector的vector一样的事情吗?并将2d数组封装到一个类中?
具体来说,此数组是我在图形程序中的zbuffer。我需要为屏幕上的每个像素存储一个z值(因此800x800的大尺寸)。
谢谢!
解决方案
回答
我们需要大约2.5兆,因此仅使用堆就可以了。除非需要调整向量的大小,否则不需要向量。有关使用" 2D"堆数组的示例,请参见C ++ FAQ Lite。
int *array = new int[800*800];
(不要忘了在完成后delete []
。)
回答
我们可以对向量进行向量处理,但这会产生一些开销。对于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的单维数组。像这样使用单个分配可能比分配800个单独的向量更有效。
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]; } };
此处显示的抽象有很多漏洞,例如,如果访问超出"行"末尾会发生什么情况? 《有效的C ++》一书对用C ++编写良好的多维数组进行了很好的讨论。
回答
有类似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;
我们可以使用简单的get和set方法将y * xwidth + x
封装在一个类中(如果我们想开始使用更高级的C ++,可以重载[]
运算符)。如果我们只是从C ++开始,而不是开始为n维数组创建可重用的完全类模板,那么我建议我们慢慢进行操作,这会让我们一开始就感到困惑。
从事图形工作后,我们可能会发现进行额外的类调用的开销可能会降低代码速度。但是,请不要担心,直到应用程序运行速度不够快,并且可以对其进行概要分析以显示丢失的时间为止,而不是使它在一开始使用时就变得更加困难,并可能带来不必要的复杂性。
我发现C ++精简版FAQ非常适合诸如此类的信息。特别是问题可以通过以下方式回答:
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16
回答
好吧,以Niall Ryan的工作为基础,如果性能成为问题,则可以通过优化数学并将其封装到类中来进一步迈出这一步。
因此,我们将从数学开始。回想一下,可以用2的幂来写800:
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]; };
回答
到目前为止,每个帖子都由程序员负责内存管理。这可以并且应该避免。 ReaperUnreal非常接近我要做的事,除了我将使用向量而不是数组,而且还使尺寸模板参数并更改访问函数-哦,IMNSHO可以将内容整理一下:
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; };
现在,我们可以在堆栈上分配此二维数组:
void foo() { Array2D<int, 800, 800> zbuffer; // Do something with zbuffer... }
我希望这有帮助!
编辑:从Array2D :: buffer
中删除了数组规范。感谢Andreas抓住了这一点!
回答
我们可以做的一件事是使用VC更改堆栈大小(如果我们确实想要堆栈上的数组),则要执行此操作的标志是[/ F] tdkhxaks(VS.80).aspx)。
但是我们可能想要的解决方案是将内存放在堆中而不是堆栈中,因为我们应该使用vectors的vector。
下一行声明了一个包含800个元素的"向量",每个元素是一个包含800个" int" s的"向量",使我们不必手动管理内存。
std::vector<std::vector<int> > arr(800, std::vector<int>(800));
请注意两个分隔尖括号(>>
)之间的空间,以便将其与右移运算符区分开(在C ++ 0x中将不再需要)。
回答
凯文(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; }
回答
或者我们可以尝试类似的方法:
boost::shared_array<int> zbuffer(new int[width*height]);
我们仍然应该也可以这样做:
++zbuffer[0];
无需再担心管理内存,无需照顾自定义类,并且很容易乱扔。
回答
如果只需要一个实例,则可以在静态存储上分配数组(在文件范围内,或者在函数范围内添加" static"限定符)。
int array[800][800]; void fn() { static int array[800][800]; }
这样,它就不会进入堆栈,我们也不必处理动态内存。