Câu trả lời:
const_iterator
s không cho phép bạn thay đổi các giá trị mà chúng trỏ tới, iterator
s thường làm.
Như với tất cả mọi thứ trong C ++, luôn luôn thích const
, trừ khi có lý do chính đáng để sử dụng các trình vòng lặp thông thường (nghĩa là bạn muốn sử dụng thực tế là chúng không const
thay đổi giá trị trỏ).
Họ nên khá nhiều để tự giải thích. Nếu iterator trỏ đến một phần tử của loại T, thì const_iterator trỏ đến một phần tử của loại 'const T'.
Về cơ bản, nó tương đương với các loại con trỏ:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
Một iterator const luôn trỏ đến cùng một phần tử, vì vậy chính iterator là const. Nhưng phần tử mà nó trỏ đến không phải là const, vì vậy phần tử mà nó trỏ đến có thể được thay đổi. Một const_iterator là một trình vòng lặp trỏ đến một phần tử const, do đó, trong khi chính trình lặp đó có thể được cập nhật (chẳng hạn như tăng hoặc giảm), thì phần tử mà nó trỏ đến không thể thay đổi.
const iterater
và const_iterator
.
Thật không may, rất nhiều phương thức cho các bộ chứa STL lấy các vòng lặp thay vì const_iterators làm tham số. Vì vậy, nếu bạn có một const_iterator , theo ý kiến của tôi, bạn không thể nói "chèn một phần tử trước phần tử mà trình lặp này trỏ tới" (theo ý kiến của một điều đó không phải là vi phạm const về mặt khái niệm). Nếu bạn muốn làm điều đó bằng mọi cách, bạn phải chuyển đổi nó thành một trình lặp không phải const bằng cách sử dụng std :: advanced () hoặc boost :: next () . Ví dụ. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Nếu container là danh sách std :: thì thời gian chạy cho cuộc gọi đó sẽ là O (n) .
Vì vậy, quy tắc phổ quát để thêm const bất cứ nơi nào "hợp lý" để làm như vậy, sẽ ít phổ biến hơn khi nói đến các container STL.
Tuy nhiên, các thùng chứa boost mất const_iterators (ví dụ: boost :: unordered_map :: erase ()). Vì vậy, khi bạn sử dụng các thùng chứa boost, bạn có thể "const agressive". Nhân tiện, có ai biết nếu hoặc khi nào các thùng chứa STL sẽ được sửa chữa không?
vector
và deque
, việc chèn một phần tử làm mất hiệu lực tất cả các trình vòng lặp hiện có, điều này không đúng const
. Nhưng tôi thấy quan điểm của bạn. Các hoạt động như vậy được bảo vệ bởi độ chứa const
, không phải các vòng lặp. Và tôi tự hỏi tại sao không có chức năng chuyển đổi vòng lặp const-to-nonconst trong giao diện bộ chứa tiêu chuẩn.
int const * foo;
int * const foo;
và int const * const foo;
cả ba đều hợp lệ và hữu ích, mỗi cách theo cách riêng của chúng. std::vector<int> const bar
nên giống như cái thứ hai, nhưng thật không may, nó thường được coi như cái thứ ba. Nguyên nhân cốt lõi của vấn đề là chúng ta không thể nói std::vector<int const> bar;
khi nào có nghĩa là không có cách nào để có được hiệu ứng tương tự như int const *foo;
trong một vectơ.
Ví dụ runnable tối thiểu
Các trình lặp không phải const cho phép bạn sửa đổi những gì chúng trỏ đến:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Lặp đi lặp lại không:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
Như đã trình bày ở trên, v.begin()
là const
quá tải, và trả về một trong hai iterator
hoặc const_iterator
tùy thuộc vào const-Ness của biến container:
Một trường hợp phổ biến khi const_iterator
bật lên là khi this
được sử dụng bên trong một const
phương thức:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const
làm this
const, mà làm cho this->v
const.
Bạn thường có thể quên nó đi auto
, nhưng nếu bạn bắt đầu vượt qua các vòng lặp đó, bạn sẽ cần phải nghĩ về chúng cho các chữ ký phương thức.
Giống như const và non-const, bạn có thể chuyển đổi dễ dàng từ non-const sang const, nhưng không phải là cách khác:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Cái nào sẽ sử dụng: tương tự như const int
vs int
: thích const iterators bất cứ khi nào bạn có thể sử dụng chúng (khi bạn không cần sửa đổi container với chúng), để ghi lại ý định đọc mà không cần sửa đổi.
(như những người khác đã nói) const_iterator không cho phép bạn sửa đổi các phần tử mà nó trỏ tới, điều này rất hữu ích bên trong các phương thức lớp const. Nó cũng cho phép bạn thể hiện ý định của mình.
ok Hãy để tôi giải thích nó với ví dụ rất đơn giản trước mà không sử dụng trình lặp liên tục xem xét chúng tôi có bộ sưu tập số nguyên ngẫu nhiên "RandomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
Như có thể thấy để ghi / chỉnh sửa dữ liệu bên trong bộ sưu tập Trình lặp thông thường được sử dụng nhưng với mục đích đọc, trình lặp không đổi đã được sử dụng. Nếu bạn thử sử dụng iterator liên tục trong vòng lặp đầu tiên, bạn sẽ gặp lỗi. Như một quy tắc ngón tay cái sử dụng iterator liên tục để đọc dữ liệu bên trong bộ sưu tập.