1. Vấn đề nó giải quyết
Sau values.clear(), vector không còn phần tử nhưng vẫn giữ nguyên capacity để dùng lại. Hãy tách số phần tử đang sống (size) khỏi dung lượng đã dành (capacity) và các con trỏ/iterator còn được phép dùng.
2. Kiến thức cần có
Biết
std::vector,size(), chỉ số phần tử và iterator.Biết destructor kết thúc vòng đời đối tượng (lifetime); vùng nhớ còn tồn tại không có nghĩa đối tượng cũ vẫn sống.
3. Ý tưởng cốt lõi
empty() chỉ quan sát size() == 0. clear() hủy mọi phần tử và đặt size về 0, không giảm capacity. Giữ capacity giúp những lần thêm tiếp theo tái sử dụng vùng nhớ; nó không giữ các phần tử cũ để đọc lại.
shrink_to_fit() là yêu cầu giảm capacity về size mà thư viện không bắt buộc thực hiện. Nếu cấp phát lại, mọi con trỏ, tham chiếu và iterator cũ vào vector đều mất hiệu lực. Quy tắc capacity của vector.
4. Cú pháp tối thiểu
std::vector<int> values{10, 20, 30};
const auto before = values.capacity();
values.clear();Cần <vector>. Sau đó values.empty() là true, values.size() là 0, và values.capacity() == before. Truy cập values[0] lúc này không hợp lệ.
5. Cách nó hoạt động
empty(),capacity(): chỉ quan sát trạng thái; không đổi phần tử hay hiệu lực của tham chiếu/iterator.clear(): hủy mọi phần tử, giữ capacity. Mọi tham chiếu/iterator tới phần tử cũ vàend()cũ mất hiệu lực.erase(pos)hoặc xóa đoạn không rỗng: xóa phần tử được chọn, dồn phần sau, giữ capacity. Tham chiếu/iterator tại và sau vị trí xóa mất hiệu lực, gồmend()cũ.resize(n)vớin < size(): hủy phần đuôi, giữ capacity. Phần trướcncòn hiệu lực; phần bị hủy vàend()cũ mất hiệu lực.resize(n)để tăng size: thêm phần tử. Nếu cấp phát lại, tất cả tham chiếu/iterator cũ mất hiệu lực; nếu không, phần tử cũ còn hiệu lực nhưngend()cũ mất hiệu lực.shrink_to_fit(): có thể giảm capacity mà không đổi size. Nếu cấp phát lại, tất cả tham chiếu/iterator cũ mất hiệu lực; nếu không, chúng giữ nguyên hiệu lực.
resize(0) cũng xóa mọi phần tử, nhưng overload resize(n) còn có yêu cầu kiểu để thêm phần tử khi tăng size. Dùng clear() nếu mục đích chỉ là xóa hết.
Việc hủy vector trả vùng nhớ qua allocator. Điều đó không bảo đảm hệ điều hành nhận lại ngay hoặc mức dùng RAM của tiến trình giảm ngay.
6. Lỗi thường gặp
Cho rằng
empty()là lệnh làm rỗng vector.Gọi
reserve(n)rồi truy cập chỉ số tớin - 1: reserve chỉ dành vùng nhớ, không tạo phần tử.Dùng con trỏ cũ sau
clear()vì capacity không đổi.Giữ iterator qua
erasemà không xét vị trí xóa.Khẳng định
shrink_to_fit()luôn giảm capacity đúng về size, hoặc phép gán vector luôn giải phóng vùng cấp phát cũ.
7. Khi nào nên dùng
Dùng clear() khi xử lý các lô dữ liệu lặp lại và muốn tái sử dụng vùng nhớ. Dùng erase để xóa một phần, resize để thay số phần tử sống. Chỉ yêu cầu thu nhỏ sau khi cân nhắc nhu cầu bộ nhớ và chi phí cấp phát lại.
8. Ví dụ đơn giản
Mã mẫu C++20 in dòng đầu:
true 0 trueBa giá trị lần lượt xác nhận vector rỗng, size bằng 0 và capacity không đổi sau clear(). Dòng thứ hai in capacity sau shrink_to_fit(); không thể ấn định một số cụ thể cho mọi implementation.
Nếu đang giữ auto p = &values[0] trước khi xóa, không dùng p để đọc phần tử cũ sau đó.
Mã mẫu hoàn chỉnh
Tệp mã nguồn
dailycppinterview/263_vector-empty-clear-and-capacity/main.cpp
// Real-World C++ Interviews Q263: How do vector::empty, clear, erase, resize, capacity, and
// shrink_to_fit differ in observation, element lifetime, memory reuse, and invalidation?
// Key: empty only observes whether size is zero. clear destroys every element and sets size to
// zero while retaining capacity for reuse; resize(0) also removes every element without being a
// request to reduce capacity. erase removes a selected range and shifts later elements,
// invalidating iterators and references at or after the erased position. References and
// iterators to elements destroyed by clear or resize are invalid, and the past-the-end iterator
// changes. shrink_to_fit is a non-binding request to reduce capacity to size; if it
// reallocates, every pointer, reference, and iterator into the vector is invalidated.
// Destroying the vector releases its owned allocation through its allocator; assigning
// replacement contents may reuse existing capacity and does not guarantee deallocation.
// Retaining capacity is often desirable for repeated workloads, so release memory only when
// measurements and ownership boundaries justify it.
#include <iostream>
#include <vector>
int main() {
std::vector<int> values{10, 20, 30};
const auto reusable_capacity = values.capacity();
values.clear();
std::cout << std::boolalpha
<< values.empty() << ' '
<< values.size() << ' '
<< (values.capacity() == reusable_capacity) << '\n';
values.shrink_to_fit();
std::cout << values.capacity() << '\n';
}
9. Điều cần nhớ
Size đếm đối tượng đang sống; capacity đếm chỗ đã dành. Khi sửa vector, kiểm tra cả vòng đời phần tử và quy tắc mất hiệu lực của tham chiếu/iterator.
10. Câu hỏi tự kiểm tra
Đề đầy đủ: vector::empty, clear, erase, resize, capacity và shrink_to_fit khác nhau thế nào về observation, element lifetime, memory reuse và invalidation?
Sau clear(), vì sao capacity giữ nguyên mà values[0] không dùng được? Nếu xóa phần tử thứ hai bằng erase, tham chiếu tới phần tử đầu còn hợp lệ không? Có thể dự đoán chính xác capacity sau shrink_to_fit() không?