2008-09-14 26 views
8

Tôi cần phải tạo một mảng 2D int có kích thước 800x800. Nhưng làm như vậy tạo ra một tràn ngăn xếp (ha ha).Cách làm việc xung quanh một mảng 2d rất lớn trong C++

Tôi mới sử dụng C++, vậy tôi có nên làm gì đó giống như vec tơ vectơ không? Và chỉ đóng gói mảng 2d vào một lớp?

Cụ thể, mảng này là zbuffer của tôi trong chương trình đồ họa. Tôi cần lưu trữ giá trị z cho mỗi pixel trên màn hình (do đó kích thước lớn là 800x800).

Cảm ơn!

Trả lời

11

Bạn cần khoảng 2,5 megabyte, vì vậy chỉ cần sử dụng vùng lưu trữ sẽ ổn thôi. Bạn không cần một vector trừ khi bạn cần phải thay đổi kích cỡ nó. Xem C++ FAQ Lite để biết ví dụ về cách sử dụng mảng heap "2D".

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

(Đừng quên delete[] nó khi bạn đã hoàn tất.)

+0

Bạn không cần phải 'mới' bộ nhớ. Có lẽ là zbuffer sẽ cần phải sống qua các chức năng. Khai báo nó toàn cầu (hoặc nếu bạn đã bắt đầu với một số lớp, trong lớp tôi giả sử). Bạn có thể làm một cái gì đó như 'int zBuffer [WIDTH * HEIGHT];' giả định rằng chiều rộng và chiều cao không thay đổi. – Mark

+2

Đã được cấp, nhưng tôi sẽ không đề xuất sử dụng toàn cầu vì đó không phải là giải pháp tốt nói chung - nó không cung cấp cho bạn nhiều sự linh hoạt về phạm vi và tuổi thọ. Nó có thể làm việc tốt cho người hỏi này vì vậy hãy tiếp tục và để lại câu trả lời của riêng bạn. –

2

Bạn có thể làm một vector của vector, nhưng điều đó sẽ có một số chi phí. Đối với một bộ đệm z, phương pháp điển hình hơn là tạo một mảng có kích thước 800 * 800 = 640000.

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

Sau đó truy cập vào các điểm ảnh như sau:

unsigned int z = z_buffer[y*width+x]; 
2

tôi có thể tạo ra một mảng không gian duy nhất của 800 * 800. Có lẽ hiệu quả hơn khi sử dụng một phân bổ đơn như thế này, thay vì phân bổ 800 vector riêng biệt.

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

Sau đó, có thể đóng gói trong lớp có vai trò như mảng 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]; 
    } 
}; 

Sự trừu tượng được hiển thị ở đây có nhiều lỗ, ví dụ: điều gì xảy ra nếu bạn truy cập vào cuối hàng "hàng"? Cuốn sách "Effective C++" có một cuộc thảo luận khá tốt về viết các mảng đa chiều tốt trong C++.

+0

toán tử [] trả về một con trỏ int? Có thể đây là ý của bạn: toán tử int * [] (size_t y) {return & ary [y * width]; } Sau đó, bạn có thể viết: my2DArray [y] [x] Mặc dù thực tế tốt hơn sẽ được sử dụng toán tử(): int & operator [] (int x, int y) {return ary [y * chiều rộng + x]; } – Niall

+0

Điểm tốt, tuy nhiên tôi không thực sự đồng ý rằng quá tải toán tử cuộc gọi hàm là thực hành tốt hơn. Nếu bạn đang gói một mảng, bạn nên làm cho nó trông giống như một mảng. –

+0

Câu hỏi thường gặp C++ có một số giải thích cho việc sử dụng toán tử(). http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.11 – Niall

1

Có C như cách thực hiện:

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; 

Bạn có thể gói gọn các y * xwidth + x bên trong một lớp học với một get dễ dàng và phương pháp thiết lập (có thể với quá tải toán tử [] nếu bạn muốn bắt đầu đi vào cao cấp hơn C++). Tôi khuyên bạn nên làm điều này từ từ mặc dù nếu bạn chỉ mới bắt đầu với C++ và không bắt đầu tạo các mẫu hoàn toàn có thể sử dụng lại cho các mảng n-dimension, điều này sẽ gây nhầm lẫn cho bạn khi bạn bắt đầu.

Ngay sau khi bạn tham gia vào công việc đồ họa, bạn có thể thấy rằng chi phí của việc có các cuộc gọi cấp thêm có thể làm chậm mã của bạn. Tuy nhiên đừng lo lắng về điều này cho đến khi ứng dụng của bạn không đủ nhanh và bạn có thể cấu hình nó để hiển thị thời gian bị mất, thay vì làm cho nó khó sử dụng hơn khi bắt đầu với độ phức tạp không cần thiết.

Tôi thấy rằng Câu hỏi thường gặp về Lite Lite của C++ rất tuyệt vời cho các thông tin như thế này.Đặc biệt câu hỏi của bạn được trả lời bởi:

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

+0

nếu bạn truyền mảng mới thành "con trỏ tới mảng 800 ints" (int [800] * buff; // ??) bạn có thể tránh [x * w + y] kludge – BCS

+0

@BCS: Điều đó không có tác dụng , không có mảng 800 con trỏ. – Niall

+0

Có một lỗi trong mã này, nó sẽ thất bại khi chiều rộng và chiều cao không bằng nhau. mảng [x * xwidth + y] phải là mảng [y * xwidth + x]. – Niall

-1

Vâng, xây dựng trên những gì Niall Ryan bắt đầu, nếu hiệu suất là một vấn đề, bạn có thể mất một bước xa hơn bằng cách tối ưu toán học và đóng gói này vào một lớp.

Vì vậy, chúng tôi sẽ bắt đầu với một chút toán học. Nhớ lại rằng 800 có thể được viết theo lũy thừa của 2 như:

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

Vì vậy, chúng ta có thể viết hàm giải quyết của chúng tôi như:

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

Vì vậy, nếu chúng ta đóng gói tất cả mọi thứ vào một lớp học thoải mái, chúng tôi nhận được:

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]; 
}; 
+0

Bạn có thể nói "tối ưu hóa sớm" không? Tại sao không để lại những loại thủ thuật ưa thích lên đến trình biên dịch? –

+0

Có thêm 2 bổ sung và 3 ca thực sự nhanh hơn so với thực hiện nhân lên không? Trình biên dịch có nhận thấy một nhân với một const và tự làm điều này nếu nó thực sự nhanh hơn? Ngoài ra, nếu bạn thay đổi const, bạn phải nhớ viết lại mã shift/add. – rjmunro

+0

Tôi nghĩ rằng việc tạo bảng tra cứu con trỏ hàng đệm sẽ nhanh hơn. Bạn có thể tạo nó trong constructor lớp dựa trên kích thước bộ đệm. – macbirdie

10

Mọi bài đăng từ trước đến nay đều để lại khả năng quản lý bộ nhớ cho lập trình viên. Điều này có thể và nên tránh. ReaperUnreal là darn gần với những gì tôi muốn làm, ngoại trừ tôi muốn sử dụng một vector chứ không phải là một mảng và cũng làm cho các tham số mẫu kích thước và thay đổi các chức năng truy cập - và oh chỉ IMNSHO làm sạch mọi thứ lên một chút:

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; 
}; 

Bây giờ bạn có thể phân bổ mảng 2-D này trên stack tốt:

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

    // Do something with zbuffer... 
} 

tôi hy vọng điều này sẽ giúp!

CHỈNH SỬA: Đã xóa thông số mảng từ Array2D::buffer. Cảm ơn Andreas vì đã bắt được nó!

+0

Tôi hoàn toàn không đồng ý về quản lý bộ nhớ _ đối với một số môi trường nhất định_. Tùy thuộc vào các mẫu phân bổ của bạn, trình phân bổ mặc định có thể phân mảnh bộ nhớ một cách khủng khiếp, dẫn đến hiệu suất khó chẩn đoán. Tôi cũng không hiểu sự cần thiết phải biến một mảng thành một lớp học. Obfuscation IMHO. – Mark

+0

Đối với một số môi trường nhất định ... OK. Tuy nhiên, thật dễ dàng để thay đổi Array2D :: buffer để sử dụng phân bổ tùy chỉnh - tốt, nếu bạn biết trình phân bổ mặc định là khủng khiếp và có những cái tốt hơn, thì bạn cũng có thể biết cách thêm phân bổ tùy chỉnh. – Kevin

+0

Theo như sử dụng một lớp ... tốt, để tránh sử dụng ngăn xếp và tránh sử dụng quản lý bộ nhớ thủ công, bạn phải sử dụng một lớp. Thats tất cả để có nó. Nếu bạn mở để quản lý bộ nhớ thủ công, hãy sử dụng 'mới' để phân bổ mảng 2-D. – Kevin

1

Một điều bạn có thể làm là thay đổi kích thước ngăn xếp (nếu bạn thực sự muốn các mảng trên stack) với VC cờ để làm điều này là [/ F] (http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.80).aspx).

Nhưng giải pháp có thể bạn muốn là đặt bộ nhớ trong đống chứ không phải là trên stack, cho rằng bạn nên sử dụng một vector của vectors.

các dòng sau tuyên bố một vector 800 yếu tố, mỗi phần tử là một s và giúp bạn tiết kiệm quản lý bộ nhớ theo cách thủ công.

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

Lưu ý khoảng cách giữa hai dấu ngoặc nhọn đóng (> >) được yêu cầu để phân biệt nó với toán tử dịch phải (sẽ không còn cần thiết trong C++0x).Ví dụ

4

Kevin là tốt, tuy nhiên:

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

Nên

std::vector<T> buffer; 

Mở rộng nó một chút bạn có thể tất nhiên thêm nhà điều hành quá tải thay vì tại() - chức năng:

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

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

Ví dụ:

int main() 
{ 
    Array2D<int, 800, 800> a; 
    a(10, 10) = 50; 
    std::cout << "A(10, 10)=" << a(10, 10) << std::endl; 
    return 0; 
} 
1

Hoặc bạn có thể thử một cái gì đó như:

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

Bạn vẫn sẽ có thể làm điều này quá:

++zbuffer[0]; 

Không nhiều lo lắng hơn về việc quản lý bộ nhớ, không có lớp học tùy chỉnh để chăm sóc, và nó dễ dàng để ném xung quanh.

1

Bạn có thể phân bổ mảng trên bộ nhớ tĩnh (trong phạm vi tệp hoặc thêm static vòng loại trong phạm vi chức năng), nếu bạn chỉ cần một phiên bản.

int array[800][800]; 

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

Bằng cách này nó sẽ không đi vào ngăn xếp và bạn không phải đối phó với bộ nhớ động.

Các vấn đề liên quan