std :: vector so với std :: mảng trong C ++


283

Sự khác biệt giữa a std::vectorvà an std::arraytrong C ++ là gì? Khi nào nên được ưu tiên hơn một? Những ưu và nhược điểm của mỗi là gì? Tất cả sách giáo khoa của tôi làm là liệt kê chúng giống nhau như thế nào.


1
Tôi đang tìm kiếm một so sánh so std::vectorvới std::arrayvà các điều khoản khác nhau như thế nào.
Zud

Zud, std::arraykhông giống như một mảng C ++. std::arraylà một trình bao bọc rất mỏng xung quanh các mảng C ++, với mục đích chính là ẩn con trỏ khỏi người dùng của lớp. Tôi sẽ cập nhật câu trả lời của tôi.
ClosCowboy

Tôi đã cập nhật tiêu đề câu hỏi và văn bản để phản ánh sự làm rõ của bạn.
Matteo Italia

Câu trả lời:


318

std::vectorlà một lớp mẫu đóng gói một mảng động 1 , được lưu trữ trong heap, tự động phát triển và co lại nếu các phần tử được thêm hoặc xóa. Nó cung cấp tất cả các hook ( begin(),, end()iterators, v.v.) làm cho nó hoạt động tốt với phần còn lại của STL. Nó cũng có một số phương thức hữu ích cho phép bạn thực hiện các thao tác trên một mảng bình thường sẽ cồng kềnh, chẳng hạn như chèn các phần tử vào giữa một vectơ (nó xử lý tất cả công việc di chuyển các phần tử sau trong hậu trường).

Vì nó lưu trữ các phần tử trong bộ nhớ được phân bổ trên heap, nó có một số chi phí liên quan đến mảng tĩnh.

std::arraylà một lớp mẫu đóng gói một mảng có kích thước tĩnh, được lưu trữ bên trong chính đối tượng, điều đó có nghĩa là, nếu bạn khởi tạo lớp trên ngăn xếp, thì chính mảng đó sẽ nằm trên ngăn xếp. Kích thước của nó phải được biết tại thời điểm biên dịch (nó được truyền dưới dạng tham số mẫu) và nó không thể tăng hoặc thu hẹp.

Nó hạn chế hơn std::vector, nhưng nó thường hiệu quả hơn, đặc biệt là đối với các kích thước nhỏ, vì trong thực tế, nó chủ yếu là một lớp bao bọc nhẹ xung quanh một mảng kiểu C. Tuy nhiên, nó an toàn hơn, vì chuyển đổi ngầm thành con trỏ bị vô hiệu hóa và nó cung cấp nhiều chức năng liên quan đến STL của std::vectorvà các container khác, vì vậy bạn có thể sử dụng dễ dàng với thuật toán & đồng STL. Nhưng dù sao, đối với giới hạn của kích thước cố định, nó kém linh hoạt hơn nhiều std::vector.

Để giới thiệu về std::array, hãy xem bài viết này ; để giới thiệu nhanh về std::vectorvà các hoạt động có thể có trên đó, bạn có thể muốn xem tài liệu của nó .


  1. Trên thực tế, tôi nghĩ rằng trong tiêu chuẩn, chúng được mô tả về độ phức tạp tối đa của các hoạt động khác nhau (ví dụ: truy cập ngẫu nhiên trong thời gian không đổi, lặp lại tất cả các yếu tố trong thời gian tuyến tính, thêm và loại bỏ các yếu tố ở cuối thời gian khấu hao không đổi, v.v.), nhưng AFAIK không có phương pháp nào khác để đáp ứng các yêu cầu đó ngoài việc sử dụng một mảng động. Như @Lucretiel đã nêu, tiêu chuẩn thực sự yêu cầu các phần tử được lưu trữ liên tục, do đó, nó là một mảng động, được lưu trữ trong đó bộ cấp phát liên quan đặt nó.

6
Về chú thích của bạn: Mặc dù đúng, tiêu chuẩn cũng đảm bảo con trỏ số học trên các yếu tố bên trong hoạt động, điều đó có nghĩa là nó phải là một mảng: & vec [9] - & vec [3] == 6 là đúng.
Lucretiel

9
Tôi khá chắc chắn, vectơ đó không tự động thu nhỏ, nhưng vì C ++ 11, bạn có thể gọi shr_to_fit.
Dino

Tôi hoàn toàn bối rối bởi thuật ngữ mảng tĩnh và tôi không chắc thuật ngữ đúng là gì. Bạn có nghĩa là một mảng kích thước tĩnh và không phải là một mảng biến tĩnh (một mảng sử dụng lưu trữ tĩnh). stackoverflow.com/questions/2672085/ . Thuật ngữ chính xác là gì? Là mảng tĩnh là một thuật ngữ cẩu thả cho một mảng với kích thước cố định?
Z boson

3
@Zboson: chắc chắn không chỉ bạn, tĩnh là một thuật ngữ bị lạm dụng; chính statictừ khóa trong C ++ có ba ý nghĩa không liên quan khác nhau và thuật ngữ này cũng thường được sử dụng để nói về những thứ được cố định tại thời điểm biên dịch. Tôi hy vọng rằng "kích thước tĩnh" rõ ràng hơn một chút.
Matteo Italia

3
Một điều cần lưu ý: Đối với lập trình thời gian thực (nơi bạn đang không được phép có bất kỳ giao năng động / deallocation sau khi khởi động) std :: mảng có lẽ sẽ được ưa thích hơn std :: vector.
TED

17

Sử dụng std::vector<T>lớp:

  • ... cũng nhanh như sử dụng mảng tích hợp, giả sử bạn chỉ làm những việc mà mảng tích hợp cho phép bạn làm (đọc và ghi vào các phần tử hiện có).

  • ... Tự động thay đổi kích thước khi các phần tử mới được chèn.

  • ... Cho phép bạn chèn các phần tử mới ở đầu hoặc ở giữa vectơ, tự động "dịch chuyển" phần còn lại của các phần tử "lên" (điều đó có hợp lý không?). Nó cũng cho phép bạn loại bỏ các phần tử ở bất cứ đâu trong std::vector, tự động chuyển phần còn lại của các phần tử xuống.

  • ... Cho phép bạn thực hiện đọc kiểm tra phạm vi bằng at()phương thức (bạn luôn có thể sử dụng các bộ chỉ mục []nếu bạn không muốn kiểm tra này được thực hiện).

hai ba lưu ý chính để sử dụng std::vector<T>:

  1. Bạn không có quyền truy cập đáng tin cậy vào con trỏ bên dưới, đây có thể là một vấn đề nếu bạn đang xử lý các hàm của bên thứ ba yêu cầu địa chỉ của một mảng.

  2. Các std::vector<bool>lớp học là ngớ ngẩn. Nó được thực hiện như một bitfield cô đọng, không phải là một mảng. Tránh nó nếu bạn muốn một mảng của bools!

  3. Trong quá trình sử dụng, std::vector<T>s sẽ lớn hơn một chút so với mảng C ++ có cùng số phần tử. Điều này là do họ cần theo dõi một lượng nhỏ thông tin khác, chẳng hạn như kích thước hiện tại của họ và vì bất cứ khi nào std::vector<T>thay đổi kích thước, họ sẽ dành nhiều không gian hơn sau đó họ cần. Điều này là để ngăn chúng khỏi phải thay đổi kích thước mỗi khi một phần tử mới được chèn vào. Hành vi này có thể được thay đổi bằng cách cung cấp một tùy chỉnh allocator, nhưng tôi không bao giờ cảm thấy cần phải làm điều đó!


Chỉnh sửa: Sau khi đọc câu trả lời của Zud cho câu hỏi, tôi cảm thấy mình nên thêm điều này:

Các std::array<T>lớp không giống như một ++ mảng C. std::array<T>là một trình bao bọc rất mỏng xung quanh các mảng C ++, với mục đích chính là ẩn con trỏ khỏi người dùng của lớp (trong C ++, các mảng được ngầm định là con trỏ, thường để làm mất hiệu lực). Các std::array<T>lớp học cũng lưu trữ kích thước của nó (chiều dài), mà có thể rất hữu ích.


5
Mặt khác, nó cũng nhanh như sử dụng mảng tích hợp được phân bổ động. Mặt khác, sử dụng mảng tự động có thể có hiệu suất khác nhau đáng kể (và không chỉ trong quá trình phân bổ, vì hiệu ứng cục bộ).
Ben Voigt

4
Đối với các vectơ không bool trong C ++ 11 trở lên, bạn có thể gọi data()a std::vector<T>để lấy con trỏ bên dưới. Bạn cũng có thể chỉ cần lấy địa chỉ của phần tử 0 (được đảm bảo để hoạt động với C ++ 11, có thể sẽ hoạt động với các phiên bản trước đó).
Matt

16

Để nhấn mạnh một điểm được tạo bởi @MatteoItalia, sự khác biệt hiệu quả là nơi dữ liệu được lưu trữ. Bộ nhớ heap (bắt buộc phải có vector) yêu cầu một cuộc gọi đến hệ thống để phân bổ bộ nhớ và điều này có thể tốn kém nếu bạn đang đếm chu kỳ. Bộ nhớ ngăn xếp (có thể cho array) gần như là "không chi phí" về mặt thời gian, bởi vì bộ nhớ được phân bổ chỉ bằng cách điều chỉnh con trỏ ngăn xếp và nó được thực hiện chỉ một lần khi vào một hàm. Ngăn xếp cũng tránh sự phân mảnh bộ nhớ. Để chắc chắn, std::arraysẽ không luôn luôn ở trên ngăn xếp; nó phụ thuộc vào nơi bạn phân bổ nó, nhưng nó vẫn sẽ liên quan đến việc cấp phát bộ nhớ ít hơn từ heap so với vector. Nếu bạn có một

  • "mảng" nhỏ (dưới 100 phần tử nói) - (một ngăn xếp thông thường có dung lượng khoảng 8 MB, vì vậy đừng phân bổ nhiều hơn một vài KB trên ngăn xếp hoặc ít hơn nếu mã của bạn được đệ quy)
  • kích thước sẽ được cố định
  • thời gian tồn tại trong phạm vi hàm (hoặc là giá trị thành viên có cùng thời gian tồn tại với lớp cha)
  • bạn đang đếm chu kỳ,

chắc chắn sử dụng std::arraymột vectơ. Nếu bất kỳ yêu cầu nào là không đúng, thì hãy sử dụng a std::vector.


3
Câu trả lời tốt đẹp. "Để chắc chắn, std :: mảng sẽ không luôn ở trên ngăn xếp; nó phụ thuộc vào nơi bạn phân bổ nó" Vậy làm cách nào tôi có thể tạo một mảng std :: không nằm trên ngăn xếp với số lượng lớn các phần tử?
Trilarion

4
@Trilarion sử dụng new std::arrayhoặc biến nó thành thành viên của một lớp mà bạn sử dụng 'new` để phân bổ.
Mark Lakata

Vì vậy, điều này có nghĩa là new std::arrayvẫn mong đợi để biết kích thước của nó tại thời gian biên dịch và không thể thay đổi kích thước của nó nhưng vẫn sống trên đống?
Trilarion

Đúng. Không có một lợi thế đáng kể để sử dụng new std::arrayvs new std::vector.
Mark Lakata

12

Nếu bạn đang xem xét sử dụng mảng đa chiều, thì có một sự khác biệt bổ sung giữa std :: mảng và std :: vector. Một mảng std :: đa chiều sẽ có các phần tử được đóng gói trong bộ nhớ ở tất cả các kích thước, giống như mảng kiểu ac. Một std :: vector đa chiều sẽ không được đóng gói trong tất cả các kích thước.

Đưa ra các tuyên bố sau:

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

Một con trỏ tới phần tử đầu tiên trong mảng kiểu c (cConc) hoặc std :: mảng (aConc) có thể được lặp qua toàn bộ mảng bằng cách thêm 1 vào mỗi phần tử trước. Chúng được đóng gói chặt chẽ.

Một con trỏ tới phần tử đầu tiên trong mảng vectơ (vConc) hoặc mảng con trỏ (ptrConc) chỉ có thể được lặp qua 5 phần tử (trong trường hợp này) đầu tiên, và sau đó có 12 byte (trên hệ thống của tôi) trên đầu cho vector tiếp theo.

Điều này có nghĩa là một mảng std :: vector> được khởi tạo thành một mảng [3] [1000] sẽ nhỏ hơn nhiều so với một mảng được khởi tạo là một mảng [1000] [3] và cả hai sẽ lớn hơn trong bộ nhớ so với std: mảng phân bổ một trong hai cách.

Điều này cũng có nghĩa là bạn không thể đơn giản chuyển một mảng vectơ đa chiều (hoặc con trỏ) sang openGL mà không tính toán chi phí bộ nhớ, nhưng bạn có thể chuyển một mảng std :: nhiều chiều sang openGL một cách ngây thơ.


-17

Vectơ là một lớp container trong khi một mảng là một bộ nhớ được cấp phát.


23
Câu trả lời của bạn dường như giải quyết std::vector<T>so với T[], nhưng câu hỏi là về std::vector<T>so với std::array<T>.
Keith Pinson
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.