Tôi muốn biết (các) sự khác biệt giữa vector's push_backvà các insertchức năng là gì.
Có khác biệt về cấu trúc không?
Có sự khác biệt về hiệu suất thực sự lớn không?
Câu trả lời:
Sự khác biệt lớn nhất là chức năng của chúng. push_backluôn đặt một phần tử mới ở cuối vectorvà insertcho phép bạn chọn vị trí của phần tử mới. Điều này ảnh hưởng đến hiệu suất. vectorcác phần tử chỉ được di chuyển trong bộ nhớ khi cần thiết để tăng độ dài của nó vì quá ít bộ nhớ được cấp cho nó. Mặt khác insertbuộc phải di chuyển tất cả các phần tử sau vị trí đã chọn của một phần tử mới. Bạn chỉ cần tạo một chỗ cho nó. Đây là lý do tại sao insertthường có thể kém hiệu quả hơn push_back.
push_back.
insertthực sự ở tại end(), vì vậy sẽ có nhiều nhánh inserthơn ở bên trong push_back, trừ khi trình biên dịch có thể nhận ra rằng chúng thực sự không cần thiết.
Các chức năng có các mục đích khác nhau. vector::insertcho phép bạn chèn một đối tượng tại một vị trí xác định trong vector, trong khi vector::push_backsẽ chỉ dán đối tượng vào cuối. Xem ví dụ sau:
using namespace std;
vector<int> v = {1, 3, 4};
v.insert(next(begin(v)), 2);
v.push_back(5);
// v now contains {1, 2, 3, 4, 5}
Bạn có thể sử dụng insertđể thực hiện công việc tương tự như push_backvới v.insert(v.end(), value).
Bên cạnh thực tế, điều push_back(x)đó tương tự như insert(x, end())(có thể với hiệu suất tốt hơn một chút), có một số điều quan trọng cần biết về các chức năng này:
push_backchỉ tồn tại trên BackInsertionSequencecác vùng chứa - vì vậy, ví dụ, nó không tồn tại trên set. Nó không thể vì push_back()cấp cho bạn mà nó sẽ luôn thêm vào cuối.FrontInsertionSequencevà họ có push_front. Điều này được hài lòng bởi deque, nhưng không phải bởi vector.insert(x, ITERATOR)từ InsertionSequence, là chung cho setvà vector. Bằng cách này, bạn có thể sử dụng sethoặc vectorlàm mục tiêu cho nhiều lần chèn. Tuy nhiên, setcó bổ sung insert(x), mà không thực tế điều tương tự (chèn đầu tiên này trong setphương tiện duy nhất để tăng tốc độ tìm kiếm nơi thích hợp bằng cách bắt đầu từ một iterator khác nhau - một tính năng không được sử dụng trong trường hợp này).Lưu ý về trường hợp cuối cùng rằng nếu bạn định thêm các phần tử vào vòng lặp, thì hãy làm container.push_back(x)và container.insert(x, container.end())sẽ thực hiện hiệu quả điều tương tự. Tuy nhiên, điều này sẽ không đúng nếu bạn nhận được điều này container.end()đầu tiên và sau đó sử dụng nó trong toàn bộ vòng lặp.
Ví dụ: bạn có thể mạo hiểm với mã sau:
auto pe = v.end();
for (auto& s: a)
v.insert(pe, v);
Điều này sẽ sao chép hiệu quả toàn bộ athành vvectơ, theo thứ tự ngược lại và chỉ khi bạn đủ may mắn để không bị phân bổ lại vectơ cho phần mở rộng (bạn có thể ngăn điều này bằng cách gọi reserve()trước); nếu bạn không may mắn như vậy, bạn sẽ nhận được cái gọi là UndefinedBehavior (tm). Về mặt lý thuyết, điều này không được phép vì các trình vòng lặp của vectơ được coi là vô hiệu mỗi khi một phần tử mới được thêm vào.
Nếu bạn làm theo cách này:
copy(a.begin(), a.end(), back_inserter(v);
nó sẽ sao chép aở cuối vtheo thứ tự ban đầu và điều này không có nguy cơ làm mất hiệu lực trình lặp.
[EDIT] Trước đây tôi đã thực hiện mã này theo cách này và đó là một sai lầm vì inserterthực sự duy trì tính hợp lệ và tiến bộ của trình lặp:
copy(a.begin(), a.end(), inserter(v, v.end());
Vì vậy, mã này cũng sẽ thêm tất cả các phần tử theo thứ tự ban đầu mà không có bất kỳ rủi ro nào.
inserterduy trì sự tiến bộ và hiệu lực của trình lặp. Tôi sẽ phải chỉnh sửa bài đăng :)
insertcó thể làm điều đó ở bất cứ đâu và bao gồm những thứ khác như hỗ trợ phạm vi.push_backthuận tiện hơn cho việc thêm vào cuối.