Có một hình ảnh nổi tiếng (cheat sheet) được gọi là "Lựa chọn container C ++". Đó là biểu đồ dòng chảy để chọn thùng chứa tốt nhất cho việc sử dụng mong muốn.
Có ai biết nếu đã có phiên bản C ++ 11 của nó không?
Đây là cái trước:
Có một hình ảnh nổi tiếng (cheat sheet) được gọi là "Lựa chọn container C ++". Đó là biểu đồ dòng chảy để chọn thùng chứa tốt nhất cho việc sử dụng mong muốn.
Có ai biết nếu đã có phiên bản C ++ 11 của nó không?
Đây là cái trước:
Câu trả lời:
Không phải là tôi biết, tuy nhiên nó có thể được thực hiện bằng văn bản tôi đoán. Ngoài ra, biểu đồ hơi tắt, vì list
nói chung không phải là một container tốt như vậy, và cũng không forward_list
. Cả hai danh sách là các container rất chuyên biệt cho các ứng dụng thích hợp.
Để xây dựng một biểu đồ như vậy, bạn chỉ cần hai hướng dẫn đơn giản:
Lo lắng về hiệu suất thường là vô dụng lúc đầu. Các cân nhắc O lớn chỉ thực sự có tác dụng khi bạn bắt đầu xử lý một vài ngàn (hoặc nhiều hơn) các mặt hàng.
Có hai loại container lớn:
find
hoạt độngvà sau đó bạn có thể xây dựng nhiều adapter trên đầu trang của họ: stack
, queue
, priority_queue
. Tôi sẽ để các bộ điều hợp ra khỏi đây, chúng đủ chuyên dụng để có thể nhận ra.
Câu 1: Liên kết ?
Câu 1.1: Đặt hàng ?
unordered_
container, nếu không hãy sử dụng đối tác được đặt hàng truyền thống của nó.Câu 1.2: Khóa riêng ?
map
, nếu không thì sử dụngset
Câu 1.3: Bản sao ?
multi
, nếu không thì không.Thí dụ:
Giả sử rằng tôi có một vài người có ID duy nhất được liên kết với họ và tôi muốn lấy dữ liệu của một người từ ID của mình một cách đơn giản nhất có thể.
Tôi muốn một find
chức năng, do đó, một container kết hợp
1.1. Tôi không thể quan tâm ít hơn về trật tự, do đó, một unordered_
container
1.2. Khóa (ID) của tôi tách biệt với giá trị được liên kết, do đó, mộtmap
1.3. ID là duy nhất, do đó không trùng lặp nên leo vào.
Câu trả lời cuối cùng là : std::unordered_map<ID, PersonData>
.
Câu 2: Bộ nhớ ổn định ?
list
Câu 2.1: Cái nào ?
list
; a forward_list
chỉ hữu ích cho dấu chân bộ nhớ ít hơn.Câu 3: Kích thước động ?
{ ... }
cú pháp), sau đó sử dụng array
. Nó thay thế mảng C truyền thống, nhưng với các chức năng thuận tiện.Câu 4: Kết thúc kép ?
deque
, nếu không sử dụng a vector
.Bạn sẽ lưu ý rằng, theo mặc định, trừ khi bạn cần một thùng chứa kết hợp, lựa chọn của bạn sẽ là một vector
. Hóa ra đó cũng là khuyến nghị của Sutter và Stroustrup .
array
không yêu cầu loại có thể xây dựng mặc định; 2) chọn multi
s không phải là quá nhiều về việc sao chép được cho phép mà nhiều hơn về việc giữ chúng có vấn đề hay không (bạn có thể đặt các bản sao vào trong các multi
thùng chứa, điều đó chỉ xảy ra khi chỉ có một bản duy nhất).
map.find(key)
ngon miệng hơn nhiều so với std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));
mặc dù vậy, điều quan trọng, về mặt ngữ nghĩa, đó find
là chức năng thành viên chứ không phải là chức năng từ <algorithm>
. Đối với O (1) so với O (log n), nó không ảnh hưởng đến ngữ nghĩa; Tôi sẽ xóa "hiệu quả" khỏi ví dụ và thay thế bằng "dễ dàng".
deque
có thuộc tính này không?
deque
yếu tố chỉ ổn định nếu bạn đẩy / bật ở hai đầu; nếu bạn bắt đầu chèn / xóa ở giữa thì tối đa N / 2 phần tử được xáo trộn để lấp đầy khoảng trống được tạo.
Tôi thích câu trả lời của Matthieu, nhưng tôi sẽ trình bày lại sơ đồ như sau:
Theo mặc định, nếu bạn cần một thùng chứa các công cụ, sử dụng std::vector
. Do đó, mọi container khác chỉ được chứng minh bằng cách cung cấp một số chức năng thay thế std::vector
.
std::vector
yêu cầu nội dung của nó có thể di chuyển được, vì nó cần có khả năng xáo trộn các vật phẩm xung quanh. Đây không phải là một gánh nặng khủng khiếp đối với nội dung (lưu ý rằng các nhà xây dựng mặc định là không bắt buộc , nhờ emplace
và vv). Tuy nhiên, hầu hết các container khác không yêu cầu bất kỳ nhà xây dựng cụ thể nào (một lần nữa, nhờ emplace
). Vì vậy, nếu bạn có một đối tượng mà bạn hoàn toàn không thể thực hiện một hàm tạo di chuyển, thì bạn sẽ phải chọn một đối tượng khác.
A std::deque
sẽ là sự thay thế chung, có nhiều thuộc tính của std::vector
, nhưng bạn chỉ có thể chèn vào hai đầu của deque. Chèn ở giữa yêu cầu di chuyển. Một std::list
nơi không có yêu cầu về nội dung của nó.
std::vector<bool>
không phải. Vâng, nó là tiêu chuẩn. Nhưng đó không phải là một vector
cách hiểu thông thường, vì các hoạt động std::vector
thường cho phép bị cấm. Và nó chắc chắn không chứa bool
s .
Do đó, nếu bạn cần vector
hành vi thực tế từ một thùng chứa bool
s, bạn sẽ không lấy nó từ đó std::vector<bool>
. Vì vậy, bạn sẽ phải thực hiện do a std::deque<bool>
.
Nếu bạn cần tìm các phần tử trong một thùng chứa và thẻ tìm kiếm không thể chỉ là một chỉ mục, thì bạn có thể cần phải từ bỏ std::vector
để ủng hộ set
và map
. Lưu ý từ khóa " có thể "; một sắp xếp std::vector
đôi khi là một sự thay thế hợp lý. Hoặc Boost.Container's flat_set/map
, thực hiện sắp xếp std::vector
.
Hiện tại có bốn biến thể trong số này, mỗi biến thể có nhu cầu riêng.
map
khi thẻ tìm kiếm không giống với mục bạn đang tìm kiếm. Nếu không thì sử dụng a set
.unordered
khi bạn có rất nhiều vật phẩm trong container và hiệu suất tìm kiếm hoàn toàn cần phải có O(1)
, hơn là O(logn)
.multi
nếu bạn cần nhiều mục để có cùng một thẻ tìm kiếm.Nếu bạn cần một thùng chứa các mặt hàng để luôn được sắp xếp dựa trên một hoạt động so sánh cụ thể, bạn có thể sử dụng a set
. Hoặc multi_set
nếu bạn cần nhiều mặt hàng để có cùng giá trị.
Hoặc bạn có thể sử dụng một sắp xếp std::vector
, nhưng bạn sẽ phải giữ nó được sắp xếp.
Khi lặp và tham chiếu bị vô hiệu đôi khi là một mối quan tâm. Nếu bạn cần một danh sách các mục, như bạn có các trình lặp / con trỏ tới các mục đó ở nhiều nơi khác nhau, thì std::vector
cách tiếp cận vô hiệu hóa có thể không phù hợp. Bất kỳ hoạt động chèn có thể gây ra vô hiệu, tùy thuộc vào kích thước và công suất hiện tại.
std::list
cung cấp một bảo đảm chắc chắn: một trình vòng lặp và các tham chiếu / con trỏ liên quan của nó chỉ bị vô hiệu khi chính mục đó bị xóa khỏi vùng chứa. std::forward_list
là có nếu bộ nhớ là một mối quan tâm nghiêm trọng.
Nếu đó là một bảo đảm quá mạnh, std::deque
cung cấp một bảo đảm yếu hơn nhưng hữu ích. Kết quả không hợp lệ từ các phần chèn vào giữa, nhưng phần chèn vào phần đầu hoặc phần đuôi chỉ gây ra sự vô hiệu của các trình vòng lặp , không phải là các con trỏ / tham chiếu đến các mục trong vùng chứa.
std::vector
chỉ cung cấp chèn giá rẻ vào cuối (và thậm chí sau đó, nó trở nên đắt tiền nếu bạn thổi công suất).
std::list
là đắt về hiệu suất (mỗi mục mới được chèn có giá phân bổ bộ nhớ), nhưng nó phù hợp . Nó cũng cung cấp khả năng đôi khi không thể thiếu để xáo trộn các mặt hàng xung quanh mà hầu như không mất chi phí hiệu năng, cũng như để trao đổi các mặt hàng với các std::list
container khác cùng loại mà không làm giảm hiệu suất. Nếu bạn cần xáo trộn mọi thứ xung quanh nhiều , hãy sử dụng std::list
.
std::deque
cung cấp chèn / loại bỏ thời gian liên tục ở đầu và đuôi, nhưng chèn ở giữa có thể khá tốn kém. Vì vậy, nếu bạn cần thêm / xóa những thứ từ phía trước cũng như phía sau, std::deque
có thể là những gì bạn cần.
Cần lưu ý rằng, nhờ vào ngữ nghĩa di chuyển, std::vector
hiệu suất chèn có thể không tệ như trước đây. Một số triển khai đã triển khai một hình thức sao chép mục dựa trên ngữ nghĩa di chuyển (cái gọi là "swaptimization"), nhưng bây giờ việc di chuyển là một phần của ngôn ngữ, nó bắt buộc theo tiêu chuẩn.
std::array
là một thùng chứa tốt nếu bạn muốn phân bổ động ít nhất có thể. Nó chỉ là một trình bao bọc xung quanh một mảng C; điều này có nghĩa là kích thước của nó phải được biết tại thời điểm biên dịch . Nếu bạn có thể sống với điều đó, sau đó sử dụng std::array
.
Điều đó đang được nói, sử dụng std::vector
và reserve
ing một kích thước sẽ làm việc tốt cho một giới hạn std::vector
. Bằng cách này, kích thước thực tế có thể khác nhau và bạn chỉ nhận được một cấp phát bộ nhớ (trừ khi bạn thổi dung lượng).
std::sort
, còn std::inplace_merge
có một điều thú vị là dễ dàng đặt các phần tử mới (thay vì một cuộc gọi std::lower_bound
+ std::vector::insert
). Rất vui được tìm hiểu về flat_set
và flat_map
!
vector<bool>
là vector<char>
.
std::allocator<T>
không hỗ trợ căn chỉnh đó (và tôi không biết tại sao lại không), thì bạn luôn có thể sử dụng công cụ cấp phát tùy chỉnh của riêng mình.
std::vector::resize
có tình trạng quá tải không lấy giá trị (nó chỉ lấy kích thước mới; mọi phần tử mới sẽ được xây dựng mặc định tại chỗ). Ngoài ra, tại sao các trình biên dịch không thể căn chỉnh chính xác các tham số giá trị, ngay cả khi chúng được tuyên bố là có sự liên kết đó?
bitset
cho bool nếu bạn biết kích thước trước en.cppreference.com/w/cpp/utility/bitset
Đây là phiên bản C ++ 11 của sơ đồ trên. [ban đầu được đăng mà không ghi công cho tác giả ban đầu của nó, Mikael Persson ]
Đây là một vòng quay nhanh, mặc dù nó có thể cần làm việc
Should the container let you manage the order of the elements?
Yes:
Will the container contain always exactly the same number of elements?
Yes:
Does the container need a fast move operator?
Yes: std::vector
No: std::array
No:
Do you absolutely need stable iterators? (be certain!)
Yes: boost::stable_vector (as a last case fallback, std::list)
No:
Do inserts happen only at the ends?
Yes: std::deque
No: std::vector
No:
Are keys associated with Values?
Yes:
Do the keys need to be sorted?
Yes:
Are there more than one value per key?
Yes: boost::flat_map (as a last case fallback, std::map)
No: boost::flat_multimap (as a last case fallback, std::map)
No:
Are there more than one value per key?
Yes: std::unordered_multimap
No: std::unordered_map
No:
Are elements read then removed in a certain order?
Yes:
Order is:
Ordered by element: std::priority_queue
First in First out: std::queue
First in Last out: std::stack
Other: Custom based on std::vector?????
No:
Should the elements be sorted by value?
Yes: boost::flat_set
No: std::vector
Bạn có thể nhận thấy rằng điều này khác rất nhiều so với phiên bản C ++ 03, chủ yếu là do tôi thực sự không thích các nút được liên kết. Các thùng chứa nút được liên kết thường có thể được đánh bại trong hiệu suất bởi một thùng chứa không được liên kết, ngoại trừ trong một vài tình huống hiếm gặp. Nếu bạn không biết những tình huống đó là gì và có quyền truy cập để tăng cường, đừng sử dụng các thùng chứa nút được liên kết. (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Danh sách này tập trung chủ yếu vào các thùng chứa nhỏ và trung bình, bởi vì (A) 99,99% so với những gì chúng ta xử lý trong mã và (B) Số lượng lớn các phần tử cần thuật toán tùy chỉnh, không phải các thùng chứa khác nhau.