Lựa chọn giữa vector :: resize () và vector :: reserved ()


151

Tôi đang phân bổ trước một số bộ nhớ cho một vectorbiến thành viên của tôi . Mã dưới đây là phần tối thiểu

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Bây giờ tại một số thời điểm, nếu t_Names.size()bằng 1000. Tôi đang có ý định tăng kích thước bằng 100. Sau đó, nếu nó đạt được 1100, một lần nữa tăng lên 100và như vậy.

Câu hỏi của tôi là, những gì để lựa chọn giữa vector::resize()vector::reserve(). Có sự lựa chọn nào tốt hơn trong loại kịch bản này?

Chỉnh sửa : Tôi có loại ước tính chính xác cho t_Names. Tôi ước tính nó là xung quanh 700để 800. Tuy nhiên, trong một số tình huống (hiếm khi), nó có thể phát triển hơn 1000.


34
Bạn nhận ra rằng làm điều này có nghĩa là sự tăng trưởng của vectơ không còn được khấu hao theo thời gian liên tục và bạn mất một trong những lợi ích hiệu suất của việc sử dụng std::vector.
Blastfurnace

1
Liên quan, xem C ++ Made Easier: Làm thế nào vectơ phát triển trên trang web của Tiến sĩ Dobbs.
jww

Câu trả lời:


262

Hai chức năng làm rất nhiều thứ khác nhau!

Các resize() phương pháp (và qua tranh luận để xây dựng tương đương với đó) sẽ chèn hoặc xóa số thích hợp của các yếu tố để các vector để làm cho kích thước nó nhất định (nó có số thứ hai tùy chọn để xác định giá trị của họ). Nó sẽ ảnh hưởng đến size(), việc lặp lại sẽ đi qua tất cả các yếu tố đó, Push_back sẽ chèn sau chúng và bạn có thể truy cập trực tiếp vào chúng bằng cách sử dụng operator[].

Các reserve()phương pháp duy nhất phân bổ bộ nhớ, nhưng lá nó uninitialized. Nó chỉ ảnh hưởngcapacity() , nhưng size()sẽ không thay đổi. Không có giá trị cho các đối tượng, bởi vì không có gì được thêm vào vector. Nếu sau đó bạn chèn các phần tử, sẽ không có sự phân bổ lại, vì nó đã được thực hiện trước, nhưng đó là hiệu ứng duy nhất.

Vì vậy, nó phụ thuộc vào những gì bạn muốn. Nếu bạn muốn một mảng 1000 mục mặc định, hãy sử dụngresize() . Nếu bạn muốn một mảng mà bạn muốn chèn 1000 mục và muốn tránh một vài phân bổ, hãy sử dụng reserve().

EDIT: Nhận xét của Blastfurnace khiến tôi đọc lại câu hỏi và nhận ra rằng trong trường hợp của bạn, câu trả lời đúng là không được phân loại theo cách thủ công. Chỉ cần tiếp tục chèn các yếu tố ở cuối khi bạn cần. Các vector sẽ tự động phân bổ lại khi cần thiết và sẽ làm điều đó hơn một cách hiệu quả hơn so với cách thủ công đề cập. Trường hợp duy nhất có reserve()ý nghĩa là khi bạn có ước tính chính xác hợp lý về tổng kích thước bạn sẽ dễ dàng có sẵn trước.

EDIT2: Chỉnh sửa câu hỏi quảng cáo: Nếu bạn có ước tính ban đầu, thì reserve()ước tính đó. Nếu nó không đủ, hãy để vector làm điều đó.


Tôi đã chỉnh sửa câu hỏi. Tôi có ước tính nhất định cho vector.
iammilind

3
@Jan: tốt, nó dễ vỡ hay không tùy theo mức độ khó khăn mà bạn đã tự làm để duy trì tài sản cần thiết. Một cái gì đó như x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }là khá mạnh mẽ khi có liên quan đến không gian. Tôi không biết có bao nhiêu yếu tố thực sự sẽ được thêm vào, nhưng tôi có giới hạn trên. Tất nhiên khi nghi ngờ, với các vectơ bạn chỉ có thể sử dụng các chỉ mục thay vì các vòng lặp, sự khác biệt thường không đáng kể.
Steve Jessop

4
Từ ngữ của bạn có ý nghĩa với ai đó đã biết câu trả lời đúng, nhưng có thể dễ dàng đánh lừa mọi người cần đặt câu hỏi. "Thay đổi kích thước () ... sẽ chèn số phần tử đã cho vào vectơ" - chỉ đúng trong lần đầu tiên sử dụng - nó thường chèn sự khác biệt giữa số được yêu cầu và số tồn tại trước đó size(). "Phương thức dự trữ () chỉ phân bổ bộ nhớ" - nó có thể hoặc không phân bổ bộ nhớ tùy thuộc vào việc capacity()đã đủ chưa, nó cũng có thể cần di chuyển các phần tử và phân bổ bộ nhớ ban đầu của chúng. "muốn tránh một vài phân bổ" và các bản sao, v.v.
Tony Delroy

18
Trên thực tế, bảo lưu trước khi đẩy là rất quan trọng và phải được sử dụng. Giả sử rằng bạn đang mã hóa một số loại trình tải mô hình 3d và mô hình có 15000 đỉnh. Nếu bạn cố gắng đẩy lùi từng đỉnh trong khi tải mà không phân bổ trước chúng, sẽ mất thời gian nghiêm trọng. Cá nhân tôi đã trải nghiệm rằng, tôi đã thử tải một mô hình xe .obj với gần 100000 đỉnh, Mất 30 giây. Sau đó, tôi đã cấu trúc lại mã bằng cách sử dụng phân bổ trước với .reserve (), bây giờ mất 3 giây. Chỉ cần đặt một .reserve (100000) vào đầu mã đã lưu 27 giây.
deniz

1
@deniz Điều đó không đúng ở quy mô 100000, nhưng rất không đúng ở thang điểm 100-300, trong đó việc bảo lưu có thể lãng phí nếu được thực hiện không cần thiết.
deworde

30

resize()không chỉ phân bổ bộ nhớ, nó còn tạo ra nhiều trường hợp như kích thước mong muốn mà bạn chuyển đến resize()làm đối số. Nhưng reserve()chỉ phân bổ bộ nhớ, nó không tạo ra các thể hiện. Đó là,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Đầu ra ( bản demo trực tuyến ):

1
1
0
1

Vì vậy, resize()có thể không được mong muốn, nếu bạn không muốn các đối tượng được tạo mặc định. Nó cũng sẽ chậm thôi. Ngoài ra, nếu bạn có push_back()các phần tử mới cho nó, size()vectơ sẽ tăng thêm bằng cách phân bổ bộ nhớ mới (cũng có nghĩa là di chuyển các phần tử hiện có sang không gian bộ nhớ mới được phân bổ). Nếu bạn đã sử dụng reserve()khi bắt đầu để đảm bảo đã có đủ bộ nhớ được phân bổ, thì size()vectơ sẽ tăng lên khi bạn push_back()sử dụng nó, nhưng nó sẽ không phân bổ lại bộ nhớ mới cho đến khi hết dung lượng mà bạn dành cho nó .


6
Sau khi làm reserve(N), chúng ta có thể sử dụng operator []vô hại. chính xác ?
iammilind

2
Mặc dù hầu hết các triển khai sẽ phân bổ số tiền chính xác mà bạn yêu cầu reserve, nhưng thông số kỹ thuật chỉ yêu cầu nó phân bổ ít nhất là nhiều, vì vậy một số triển khai có thể làm tròn đến một số ranh giới và do đó hiển thị công suất cao hơn 1000.
Jan Hudec

16
@iammilind: Không, nếu chỉ số lớn hơn hoặc bằng v.size(). Lưu ý rằng reserve(N)không thay đổi size()vector.
Nawaz

5
@iammilind: Không chính xác. Sau khi gọi reSERVE, không có mục nào được thêm vào, chỉ có đủ bộ nhớ để thêm chúng.
Jan Hudec

2

Từ mô tả của bạn, có vẻ như bạn muốn "dự trữ" không gian lưu trữ được phân bổ của vectơ t_Names.

Lưu ý rằng resizekhởi tạo vectơ mới được phân bổ trong đó reservechỉ phân bổ nhưng không xây dựng. Do đó, 'dự trữ' nhanh hơn nhiều so với 'thay đổi kích thước'

Bạn có thể tham khảo tài liệu liên quan đến sự khác biệt của thay đổi kích thướcdự trữ


1
Thay vào đó, vui lòng tham khảo tại đây: vectơdung lượng ( tại sao? )
sehe

1
Cảm ơn bạn đã thêm liên kết, sehe
nhúng

2

dự trữ khi bạn không muốn các đối tượng được khởi tạo khi dành riêng. Ngoài ra, bạn có thể thích phân biệt logic và theo dõi số lượng của nó so với số lượng sử dụng của nó khi bạn thay đổi kích thước. do đó, có một sự khác biệt về hành vi trong giao diện - vectơ sẽ biểu thị cùng số lượng phần tử khi được bảo lưu và sẽ lớn hơn 100 phần tử khi thay đổi kích thước trong kịch bản của bạn.

Có sự lựa chọn nào tốt hơn trong loại kịch bản này?

nó phụ thuộc hoàn toàn vào mục tiêu của bạn khi chiến đấu với hành vi mặc định. một số người sẽ ủng hộ các công cụ phân bổ tùy chỉnh - nhưng chúng tôi thực sự cần một ý tưởng tốt hơn về những gì bạn đang cố gắng giải quyết trong chương trình của mình để tư vấn tốt cho bạn.

fwiw, nhiều triển khai vectơ đơn giản sẽ tăng gấp đôi số lượng phần tử được phân bổ khi chúng phải tăng - bạn đang cố gắng giảm thiểu kích thước phân bổ cao điểm hay bạn đang cố gắng dành đủ không gian cho một số chương trình miễn phí khóa hoặc một cái gì khác?


" Dự trữ khi bạn không muốn các đối tượng được khởi tạo khi được bảo lưu. " Công thức chính xác là khi bạn không muốn các đối tượng tồn tại . Nó không giống như một mảng chưa được khởi tạo của một loại có thể xây dựng tầm thường, trong đó các đối tượng không thể được đọc nhưng có thể được gán cho; thay vào đó, chỉ có bộ nhớ được dành riêng, nhưng không có đối tượng nào tồn tại trong đó, vì vậy chúng không thể được truy cập bằng cách sử dụng operator[]hoặc bất cứ thứ gì.
gạch dưới
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.