std :: vector :: resize () so với std :: vector :: Reserve ()


80

Có một chủ đề trong phần ý kiến trong bài này về việc sử dụng std::vector::reserve()vs std::vector::resize().

Đây là mã gốc:

void MyClass::my_method()
{
    my_member.reserve(n_dim);
    for(int k = 0 ; k < n_dim ; k++ )
         my_member[k] = k ;
}

Tôi tin rằng để viết các phần tử trong vector, điều chính xác cần làm là gọi std::vector::resize(), không phải std::vector::reserve().

Trên thực tế, mã kiểm tra sau "gặp sự cố" trong bản dựng gỡ lỗi trong VS2010 SP1:

#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    v.reserve(10);
    v[5] = 2;

    return 0;
}

Tôi đúng, hay tôi sai? Và VS2010 SP1 là đúng, hay là sai?


11
Lời giải thích có thể là đơn giản như "Tôi đã sai": D
Luchian Grigore

7
Tôi đã gắn cờ điều này là "quá bản địa hóa", vì @LuchianGrigore hiếm khi sai
Mặc định

1
@Default đọc "hiếm khi sai" là "nhanh chóng trong việc điều chỉnh sai lầm của mình" :)
Luchian Grigore

1
Mã trong bài viết gốc đã được cập nhật để sử dụng chính xác resize()và nghi ngờ đã được xóa. Đối với người kiểm duyệt: vui lòng xóa câu hỏi này nếu nó "quá bản địa hóa" hoặc giữ nó nếu bạn nghĩ rằng nó có thể giúp ích cho người khác trong tương lai.
Mr.C64 23/10/12

1
câu hỏi này thực sự xóa nghi ngờ của tôi khi tôi di chuyển dự án của mình từ vc6 sang vs2013 .. cảm ơn :))
pengMiao

Câu trả lời:


114

Có hai phương pháp khác nhau vì một lý do:

std::vector::reserve sẽ cấp phát bộ nhớ nhưng sẽ không thay đổi kích thước vectơ của bạn, vectơ này sẽ có kích thước logic giống như trước đó.

std::vector::resizesẽ thực sự sửa đổi kích thước vectơ của bạn và sẽ lấp đầy bất kỳ khoảng trống nào bằng các đối tượng ở trạng thái mặc định của chúng. Nếu chúng là số nguyên, chúng sẽ bằng không.

Sau khi đặt trước, trong trường hợp của bạn, bạn sẽ cần nhiều push_backs để ghi vào phần tử 5. Nếu bạn không muốn làm điều đó thì trong trường hợp của bạn, bạn nên sử dụng thay đổi kích thước.

Một điều về dự trữ: nếu sau đó bạn thêm các phần tử với push_back, cho đến khi bạn đạt đến dung lượng mà bạn đã đặt trước, mọi tham chiếu, trình vòng lặp hoặc con trỏ hiện có đến dữ liệu trong vectơ của bạn sẽ vẫn hợp lệ. Vì vậy, nếu tôi đặt trước 1000 và kích thước của tôi là 5, &vec[4]sẽ vẫn giữ nguyên cho đến khi vectơ có 1000 phần tử. Sau đó, tôi có thể gọi push_back()và nó sẽ hoạt động, nhưng con trỏ được lưu trữ &vec[4]trước đó có thể không còn hợp lệ.


vì vậy, đối với vector rỗng, tức là vec, sau khi dự trữ vec [1] sẽ kết thúc với lỗi phân đoạn.
hailinzeng

2
vec [1] sẽ là hành vi không xác định.
CashCow

Sẽ std::vector::reservengăn việc sao chép không thường xuyên của toàn bộ mảng trên push_back?
Đăng tự

Điều này chỉ dành cho C ++ 11 hay một triển khai std cụ thể? Có vẻ như mã có dự trữ và truy cập bằng [] hoạt động tốt? godbolt.org/z/MhgFdZ
Steve_Corrin

1
@Steve_Corrin Hành vi không xác định là không xác định. Nó dường như có thể hoạt động. Nó vẫn là mã không hợp lệ. < size()Không cho phép lập chỉ mục tại các phần tử không tồn tại trong vùng chứa. Chúng không tồn tại ở đó, theo định nghĩa của ngôn ngữ. Nếu trình biên dịch của bạn quyết định không khởi chạy hạt nhân và thay vào đó chỉ chọc / nhìn trộm RAM theo cách bạn muốn, đó chỉ là điều may mắn. Hoặc xui xẻo, tôi đoán vậy; lý tưởng là chúng ta có thể bắt được tất cả những thứ không hợp lệ mà tất cả các lập trình viên sẽ làm, nhưng chúc bạn may mắn đã đến được đó!
underscore_d

25

Jan Hudec đã trả lời ở đây : Lựa chọn giữa vector :: resize () và vector :: Reserve ()

Hai chức năng làm những việc rất khác nhau.

Phương thức resize () (và truyền đối số tới hàm tạo tương đương với phương thức đó) sẽ chèn một số phần tử đã cho vào vectơ (nó có đối số thứ hai tùy chọn để chỉ định giá trị của chúng). Nó sẽ ảnh hưởng đến kích thước (), phép lặp sẽ đi qua tất cả các phần tử đó, push_back sẽ chèn sau chúng và bạn có thể truy cập trực tiếp chúng bằng toán tử [].

Phương thức Reserve () chỉ cấp phát bộ nhớ, nhưng để nó chưa được khởi tạo. Nó chỉ ảnh hưởng đến dung lượng (), nhưng kích thước () sẽ không thay đổi. Không có giá trị cho các đối tượng, vì không có gì được thêm vào vectơ. Nếu sau đó bạn chèn các phần tử vào, sẽ không có sự phân bổ lại nào xảy ra, bởi vì nó đã được thực hiện trước, nhưng đó là tác dụ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ụng resize (). 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 lần phân bổ, hãy sử dụng Reserve ().

CHỈNH SỬA: 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 không được phân bổ trước theo cách thủ công. Chỉ cần tiếp tục chèn các phần tử vào cuối khi bạn cần. Vectơ sẽ tự động phân bổ lại khi cần thiết và sẽ làm điều đó hiệu quả hơn so với cách thủ công đã đề cập. Trường hợp duy nhất mà dự trữ () có ý nghĩa là khi bạn có ước tính chính xác hợp lý về tổng kích thước mà bạn sẽ dễ dàng có sẵn từ 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, hơn dự trữ () ước tính đó và nếu nó không đủ, chỉ cần để vectơ làm việc đó.


12

Nó phụ thuộc vào những gì bạn muốn làm. reservekhông không thêm bất kỳ phần tử vào vector; nó chỉ thay đổi capacity(), đảm bảo rằng việc thêm các phần tử sẽ không phân bổ lại (và ví dụ: làm mất hiệu lực các trình vòng lặp). resizethêm các phần tử ngay lập tức. Nếu bạn muốn thêm phần tử sau ( insert(), push_back()), hãy sử dụng reserve. Nếu bạn muốn truy cập các phần tử sau này (sử dụng []hoặc at()), hãy sử dụng resize. Vì vậy, bạn MyClass::my_methodcó thể là:

void MyClass::my_method()
{
    my_member.clear();
    my_member.reserve( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member.push_back( k );
    }
}

hoặc là

void MyClass::my_method()
{
    my_member.resize( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member[k] = k;
    }
}

Cái nào bạn chọn là một câu hỏi về sở thích, nhưng mã bạn trích dẫn rõ ràng là không chính xác.


3

Có lẽ nên có một cuộc thảo luận về việc khi nào cả hai phương thức được gọi với một số ÍT hơn kích thước hiện tại của vector.

Gọi reserve()với số nhỏ hơn dung lượng sẽ không ảnh hưởng đến dung lượng cũng như dung lượng.

Gọi resize()với một số nhỏ hơn kích thước hiện tại, vùng chứa sẽ được giảm xuống kích thước đó để phá hủy các phần tử thừa một cách hiệu quả.

Tóm lại resize()sẽ giải phóng bộ nhớ trong khi reserve()thì không.


Thay đổi kích thước không bao giờ giải phóng bộ nhớ. Khi kích thước ngày càng nhỏ, các hàm hủy sẽ được gọi, nhưng bộ nhớ vẫn được giữ (dung lượng không thay đổi).
John Gordon

2

Vâng, bạn chính xác, Luchian chỉ mắc một lỗi chính tả và có lẽ quá mệt mỏi để nhận ra sai lầm của mình.


1

thay đổi kích thước thực sự thay đổi số lượng phần tử trong vectơ, các mục mới được tạo mặc định nếu việc thay đổi kích thước làm cho vectơ phát triển.

vector<int> v;
v.resize(10);
auto size = v.size();

trong trường hợp này kích thước là 10.

Mặt khác dự trữ chỉ yêu cầu bộ đệm bên trong được tăng lên đến kích thước được chỉ định nhưng không thay đổi "kích thước" của mảng, chỉ kích thước bộ đệm của nó được thay đổi.

vector<int> v;
v.reserve(10);
auto size = v.size();

trong trường hợp này kích thước vẫn là 0.

Vì vậy, để trả lời câu hỏi của bạn, có, bạn đúng, ngay cả khi bạn dành đủ dung lượng, bạn vẫn đang truy cập bộ nhớ chưa khởi tạo bằng toán tử chỉ mục. Với một int không quá tệ nhưng trong trường hợp là vector của các lớp, bạn sẽ truy cập vào các đối tượng chưa được xây dựng.

Kiểm tra giới hạn các trình biên dịch được đặt thành chế độ gỡ lỗi rõ ràng có thể bị nhầm lẫn bởi hành vi này, đây có thể là lý do tại sao bạn gặp sự cố.

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.