Có nên sử dụng mảng trong C ++ không?


95

Kể từ khi tồn tại std::liststd::vectortồn tại, có lý do gì để sử dụng mảng C truyền thống trong C ++, hay nên tránh chúng, giống như malloc?



18
@Als: Câu hỏi đó liên quan đến sự khác biệt giữa hai vùng chứa cụ thể, trong khi câu hỏi này liên quan đến sự khác biệt giữa mảng thô và vùng chứa tiêu chuẩn nói chung.
Jon Purdy

Câu trả lời:


109

Trong C ++ 11 nếu std::arraycó sẵn, câu trả lời là "có, nên tránh các mảng". Trước C ++ 11, bạn có thể cần sử dụng mảng C để cấp phát mảng trong bộ lưu trữ tự động (tức là trên ngăn xếp).


3
Tuy nhiên, nhiều trình biên dịch vẫn thiếu hỗ trợ C ++ 11. Bạn sẽ phải quyết định khi nào nó tốt hơn để sử dụng một trong hơn người kia do thiếu std :: mảng
Nowayz

4
std :: array là một khuôn mẫu, tác động đến các dự án lớn về thời gian xây dựng và có thể là kích thước mã vì đối với mỗi tổ hợp T, N, khuôn mẫu sẽ được khởi tạo mới.
zvrba

std :: vector đảm bảo sự liên kết dữ liệu theo tiêu chuẩn, vì vậy nó có thể được sử dụng hầu như ở mọi nơi. Với C ++ 11 thực sự không có lý do gì để sử dụng mảng C.
Nils

16
Các mảng @Nils cũng đảm bảo sự liên kết. Ngoài ra, phân bổ lưu trữ tự động ("ngăn xếp") nhanh hơn phân bổ lưu trữ động. Nếu tôi biết mình có chính xác 3 phần tử [ví dụ: tọa độ của một tam giác], thì không có lý do gì để sử dụng vector.
zvrba

9
@zvrba - Kiểm tra lắp ráp được tạo khi sử dụng mảng std :: array vs C. Không có sự khác biệt nào cả.
Nemanja Trifunovic

85

Chắc chắn, mặc dù với std::arraytrong C ++ 11, thực tế chỉ dành cho dữ liệu tĩnh. Mảng kiểu C có ba ưu điểm quan trọng hơn std::vector:

  • Chúng không yêu cầu phân bổ động. Vì lý do này, các mảng kiểu C được ưu tiên hơn khi bạn có thể có nhiều mảng rất nhỏ. Nói điều gì đó giống như một điểm thứ nguyên:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    Thông thường, người ta có thể tưởng tượng một dimssẽ rất nhỏ (2 hoặc 3), Tmột kiểu tích hợp ( double) và bạn có thể kết thúc std::vector<Point>với hàng triệu phần tử. Bạn chắc chắn không muốn hàng triệu phân bổ động là 3 đôi.

  • Hỗ trợ khởi tạo tĩnh. Đây chỉ là một vấn đề đối với dữ liệu tĩnh, trong đó một số thứ như:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    Điều này thường thích hợp hơn khi sử dụng một vectơ (và std::string), vì nó tránh tất cả các vấn đề về thứ tự khởi tạo; dữ liệu được tải trước, trước khi bất kỳ mã thực tế nào có thể được thực thi.

  • Cuối cùng, liên quan đến những điều trên, trình biên dịch có thể tính toán kích thước thực của mảng từ các trình khởi tạo. Bạn không cần phải đếm chúng.

Nếu bạn có quyền truy cập vào C ++ 11, hãy std::arraygiải quyết hai vấn đề đầu tiên và chắc chắn nên được sử dụng ưu tiên cho mảng kiểu C trong trường hợp đầu tiên. Tuy nhiên, nó không giải quyết phần thứ ba và việc có kích thước trình biên dịch cho mảng theo số lượng bộ khởi tạo vẫn là một lý do hợp lệ để thích mảng kiểu C.


11
Khởi tạo mảng kiểu C cũng loại bỏ nhu cầu tự lặp lại. int i[] = { 1, 2, 3 };tiếp tục làm việc với int i[] = { 1, 2, 3, 4 };. array<int, 3>cần được thay đổi thủ công thành array<int, 4>.

10
@JoeWreschnig Một thay đổi mà bạn có thể dễ dàng quên. Nếu bạn thêm một phần tử, trình biên dịch sẽ khiếu nại, nhưng nếu bạn xóa một phần tử, bạn sẽ kết thúc với một phần tử được khởi tạo bổ sung, 0 ở cuối. Tôi vẫn sử dụng rộng rãi các mảng kiểu C cho loại dữ liệu tĩnh này.
James Kanze

3
Câu đầu tiên không có ý nghĩa.
Konrad Rudolph

4
Điểm thứ ba có thể được giải quyết khá dễ dàng bằng cách sử dụng một make_arrayhàm , tương tự như make_pairv.v. Hat-tip thành @R. Martinho Fernandes .
Konrad Rudolph

@KonradRudolph: Chắc chắn rồi. Andreas hỏi “Có nên sử dụng mảng trong C ++ không?”, James trả lời “Chắc chắn rồi, mặc dù std::arraytrong C ++ 11, [chúng nên được sử dụng] thực tế chỉ cho dữ liệu tĩnh”.
Jon Purdy

15

Đừng bao giờ nói "không bao giờ", nhưng tôi đồng ý rằng vai trò của chúng bị giảm đi đáng kể bởi cấu trúc dữ liệu thực sự từ STL.

Tôi cũng muốn nói rằng việc đóng gói bên trong các đối tượng sẽ giảm thiểu tác động của các lựa chọn như thế này. Nếu mảng là thành viên dữ liệu riêng tư, bạn có thể hoán đổi nó vào hoặc ra mà không ảnh hưởng đến các máy khách trong lớp của bạn.


11

Tôi đã làm việc trên các hệ thống quan trọng về an toàn mà bạn không thể sử dụng phân bổ bộ nhớ động. Bộ nhớ phải luôn ở trên ngăn xếp. Do đó, trong trường hợp này, bạn sẽ sử dụng mảng vì kích thước được cố định tại thời điểm biên dịch.


8
Trước C ++ 11, tôi đã đồng ý, nhưng std::array<T>phân bổ trên các ngăn xếp và về cơ bản không có chi phí trên một mảng thô.
111111

5
@ 111111 - Đồng ý. Nhưng tôi biết một số người trong ngành đó vẫn chưa chuyển sang C ++ 11
Ed Heal

Tôi biết đó là lý do tại sao tôi không tán thành bạn, nhưng tôi nghĩ boost đã có một phiên bản và bạn cũng có thể dễ dàng triển khai.
111111

6
nhưng trong các hệ thống quan trọng về an toàn, bạn không sử dụng các tính năng trình biên dịch mới (ít được thử nghiệm hơn) và bạn không sử dụng tăng cường.
James Kanze

3
Nhiều hệ thống quan trọng về an toàn được xây dựng trên các trình biên dịch CŨ thậm chí không có các tính năng của trình biên dịch mới hơn vì thay đổi các dãy công cụ là một quá trình chậm, tốn kém, đòi hỏi rất nhiều thủ tục giấy tờ, thử nghiệm và chứng nhận.
Brian McFarland

6

arraytrong c++cung cấp cho bạn kích thước cố định thay thế nhanh chóng của kích thước động std::vectorstd::list. std :: array là một trong những phần bổ sung trong c++11. Nó cung cấp lợi ích của các thùng chứa std trong khi vẫn cung cấp ngữ nghĩa kiểu tổng hợp của các mảng kiểu C.

Vì vậy, c++11tôi chắc chắn sẽ sử dụng std::array, khi nó được yêu cầu, trên vector. Nhưng tôi sẽ tránh mảng kiểu C trong C++03.


4

Thông thường, không , tôi không thể nghĩ ra lý do để sử dụng các mảng thô vectors. Nếu mã là mới .

Bạn có thể phải sử dụng mảng nếu thư viện của bạn cần tương thích với mã yêu cầu mảng và con trỏ thô.


1
... nhưng kể từ C ++ 03, một vectơ "thực sự có" một mảng mà bạn có thể truy cập bằng con trỏ để đọc hoặc ghi. Vì vậy, điều đó bao gồm hầu hết các trường hợp mã mong đợi con trỏ đến mảng. Chỉ thực sự khi mã đó phân bổ hoặc giải phóng mảng mà bạn không thể sử dụng vector.
Steve Jessop

@SteveJessop bạn có thể truy cập vào mảng nội bộ không?
Luchian Grigore

1
@LuchianGrigore: vector.data()trong C ++ 11 hoặc &vector.front()trước đó.
Mike Seymour

@Luchian: miễn là vectơ không trống, bạn có thể đưa một con trỏ đến một phần tử (và nếu nó trống, bạn có thể chuyển một con trỏ null và độ dài 0 cho bất kỳ hàm được viết hợp lý nào chấp nhận trường hợp cạnh của một đệm có kích thước bằng không). Mục đích duy nhất của bảo đảm liên tục vectơ được thêm vào trong C ++ 03 là cho phép các vectơ được sử dụng làm bộ đệm bằng mã hướng con trỏ.
Steve Jessop

1
@SteveJessop Và thực tế là rất nhiều người nghĩ rằng nó đã được đảm bảo, và nó được coi là tốt hơn để không làm họ thất vọng.
James Kanze

4

Tôi biết rất nhiều người đang chỉ ra std :: array để cấp phát mảng trên ngăn xếp và std :: vector cho heap. Nhưng dường như không hỗ trợ căn chỉnh không phải gốc. Nếu bạn đang thực hiện bất kỳ loại mã số nào mà bạn muốn sử dụng hướng dẫn SSE hoặc VPX (do đó yêu cầu căn chỉnh 128 hoặc 256 byte tương ứng), mảng C dường như vẫn là lựa chọn tốt nhất của bạn.


3

Tôi sẽ nói rằng các mảng vẫn hữu ích, nếu bạn đang lưu trữ một lượng nhỏ dữ liệu tĩnh tại sao lại không.


2

Ưu điểm duy nhất của một mảng (tất nhiên được bao bọc trong thứ gì đó sẽ tự động quản lý vị trí giao dịch của nó khi cần) std::vectormà tôi có thể nghĩ đến là vectorkhông thể chuyển quyền sở hữu dữ liệu của nó, trừ khi trình biên dịch của bạn hỗ trợ C ++ 11 và di chuyển các hàm tạo.


6
"vector không thể chuyển quyền sở hữu dữ liệu của nó" - vâng nó có thể, trong C ++ 03, sử dụng swap.
Steve Jessop

2

Mảng kiểu C là một cấu trúc dữ liệu cơ bản, vì vậy sẽ có những trường hợp tốt hơn khi sử dụng nó. Tuy nhiên, đối với trường hợp chung, hãy sử dụng cấu trúc dữ liệu nâng cao hơn để làm tròn các góc của dữ liệu cơ bản. C ++ cho phép bạn làm một số điều rất thú vị và hữu ích với bộ nhớ, nhiều trong số đó hoạt động với các mảng đơn giản.


3
Mảng kiểu C cơ bản hơn mảng std::arrays như thế nào? Trong nhiều trường hợp, cả hai sẽ được biên dịch vào cùng một assembly.
bùng binh

1
Cơ bản hơn ở chỗ nó cơ bản hơn. Bạn biết những gì một mảng sẽ làm, std :: array có thể có các câu hỏi triển khai vì nó dựa vào thư viện chuẩn.
James Wynn

1
@JamesWynn Không hẳn. std::arraycó ngữ nghĩa được xác định chính xác được xây dựng trên đầu các mảng tĩnh.
Konrad Rudolph

1

Bạn nên sử dụng các vùng chứa STL bên trong, nhưng bạn không nên chuyển con trỏ đến các vùng chứa như vậy giữa các mô-đun khác nhau, nếu không bạn sẽ rơi vào địa ngục phụ thuộc. Thí dụ:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

là một giải pháp rất tốt nhưng không

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

Lý do là vì chuỗi std :: có thể được thực hiện theo nhiều cách khác nhau nhưng một chuỗi c-style luôn là một chuỗi c-style.


Tôi nghĩ những gì bạn đang cố gắng nói là: Không liên kết các mã đối tượng khác nhau với nhau nếu các trình biên dịch / triển khai khác nhau của thư viện chuẩn đã được sử dụng để tạo chúng. Điều đó chắc chắn đúng. Làm thế nào điều này liên quan đến câu hỏi ban đầu?
jogojapan

Đó chỉ là một lời khuyên khi sử dụng mảng hoặc vùng chứa STL. Xây dựng dữ liệu bằng cách sử dụng một vùng chứa, chuyển nó dưới dạng một mảng. Đối với dữ liệu khác mà chuỗi bạn sẽ có một cái gì đó như myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329

Nhưng những vấn đề này chỉ nảy sinh khi bạn kết hợp các triển khai khác nhau của thư viện chuẩn trong cùng một dự án. Thật điên rồ. Trong bất kỳ đoạn mã thông thường nào, bạn hoàn toàn có thể chuyển, chẳng hạn như một vectơ, bằng tham chiếu (hoặc, trong C ++ 11, di chuyển nó) vào một hàm.
jogojapan

1
Tôi chỉ tình cờ như plug-ins
user877329
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.