2015-10-03 15 views
14

Tôi đã đi qua một số mã mà phân bổ một mảng 2ngày với phương pháp sau đây:Có phải int mới [] [] là một điều hợp lệ để làm trong C++?

auto a = new int[10][10]; 

Đây có phải là một điều chính đáng để làm trong C++? Tôi có tìm kiếm thông qua một số sách tham khảo C++, không ai trong số họ đã đề cập đến cách tiếp cận như vậy. Thông thường tôi sẽ thực hiện việc phân bổ bằng tay như sau:

int **a = new int *[10]; 
for (int i = 0; i < 10; i++) { 
    a[i] = new int[10]; 
} 

Nếu cách tiếp cận đầu tiên là hợp lệ, sau đó cái nào được ưa chuộng?

+12

Nó không muốn sử dụng ' mới'. –

+0

nếu kích thước được biết trước và nó không phải là tốt hơn không sử dụng mới. –

+9

Để công bằng, có những trường hợp bạn vẫn cần sử dụng 'mới' cho loại điều này (ví dụ: gọi API cũ), vì vậy sẽ tốt hơn nếu bạn trả lời câu hỏi như được đặt ra, thay vì phân phát lời khuyên của ông già về C++ hiện đại nhất thực hành. ;-) –

Trả lời

19

Ví dụ đầu tiên:

auto a = new int[10][10]; 

Nó sẽ xác định một mảng đa chiều hoặc mảng của mảng như một khối liền kề của bộ nhớ.

Ví dụ thứ hai:

int** a = new int*[10]; 
for (int i = 0; i < 10; i++) { 
    a[i] = new int[10]; 
} 

Đó không phải là một sự thật đa chiều mảng. Đó là, trên thực tế, một mảng của của con trỏ và yêu cầu hai indirections để truy cập vào mỗi phần tử.

+0

Câu trả lời hay, ngoài "Điều này giúp truy cập nhanh hơn ví dụ thứ hai" - Tôi không nghĩ bạn thực sự có thể nói điều này với bất kỳ sự chắc chắn nào. –

+0

Đầu tiên là một mảng các mảng, thứ hai là một mảng các con trỏ. – molbdnilo

+0

@molbdnilo Tôi không bỏ qua nhận xét của bạn, chỉ cần điều tra ... – Galik

1

Trong trường hợp này, đối với các mảng nhỏ, sẽ hiệu quả hơn khi phân bổ chúng trên ngăn xếp. Có lẽ thậm chí sử dụng một wrapper tiện lợi như std::array<std::array<int, 10>, 10>. Tuy nhiên, nói chung, nó là hợp lệ để làm điều gì đó như sau:

auto arr = new int[a][b]; 

đâu a là một std::size_tb là một constexpr std::size_t. Điều này dẫn đến phân bổ hiệu quả hơn vì chỉ có một cuộc gọi đến số operator new[] với số sizeof(int) * a * b làm đối số thay vì số a gọi tới operator new[] với số sizeof(int) * b làm đối số. Như Galik đã nêu trong câu trả lời của anh ta, đó cũng là tiềm năng cho thời gian truy cập nhanh hơn, do sự kết hợp bộ nhớ cache tăng lên (toàn bộ mảng là tiếp giáp trong bộ nhớ). Tuy nhiên, lý do duy nhất tôi có thể tưởng tượng một bằng cách sử dụng một cái gì đó như thế này là với ma trận/tensor có kích thước thời gian, nơi tất cả các tham số được biết tại thời gian biên dịch, nhưng nó phân bổ trên heap nếu nó vượt quá kích thước ngăn xếp. Nói chung, có lẽ tốt nhất bạn nên viết lớp trình bao bọc RAII của riêng bạn như sau (bạn cũng cần thêm các trình truy cập khác nhau cho chiều cao/chiều rộng, cùng với việc triển khai một hàm tạo/sao chép và chuyển nhượng, nhưng ý tưởng chung là ở đây:

template <typename T> 
class Matrix { 
public: 
    Matrix(std::size_t height, std::size_t width) : m_height(height), m_width(width) 
    { 
      m_data = new T[height * width](); 
    } 

    ~Matrix() { delete m_data; m_data = nullptr; } 

public: 
    T&    operator()(std::size_t x, std::size_t y) 
    { 
      // Add bounds-checking here depending on your use-case 
      // by throwing a std::out_of_range if x/y are outside 
      // of the valid domain. 
      return m_data[x + y * m_width]; 
    } 

    const T&   operator()(std::size_t x, std::size_t y) const 
    { 
      return m_data[x + y * m_width]; 
    } 

private: 
    std::size_t  m_height; 
    std::size_t  m_width; 
    T*    m_data; 
}; 
4

Khái niệm new int[10][10] nghĩa để phân bổ một mảng trong mười phần tử kiểu int[10], vì vậy có, nó là một điều hợp lệ để làm

các loại con trỏ trả về bởi mới là int(*)[10];. người ta có thể khai báo một biến kiểu như vậy thông qua int (*ptr)[10];.

Vì lợi ích của mức độ dễ đọc, người ta có lẽ không nên sử dụng cú pháp đó, và nên thích sử dụng auto như trong ví dụ của bạn, hoặc sử dụng một typedef để đơn giản hóa như trong

using int10 = int[10]; // typedef int int10[10]; 
int10 *ptr; 
Các vấn đề liên quan