Подтвердить что ты не робот

Почему не вектор:: clear удаляет элементы из вектора?

Когда я использую clear() в std::vector, он должен уничтожить все элементы в vector, но вместо этого это не так.

Пример кода:

vector<double> temp1(4);
cout << temp1.size() << std::endl;
temp1.clear();
cout << temp1.size() << std::endl;

temp1[2] = 343.5; // I should get segmentation fault here ....

cout << "Printing..... " << temp1[2] << endl;
cout << temp1.size() << std::endl;

Теперь я должен был получить ошибку сегментации, пытаясь получить доступ к очищенному вектору, но вместо этого он заполняет значение там (которое по мне очень плохо)

Результат выглядит следующим образом:

4
0
Printing..... 343.5
0

Это нормально? Это очень сложная ошибка, которая в основном убила мой код в течение нескольких месяцев.

4b9b3361

Ответ 1

При доступе за пределы вектора вы получаете Undefined Behavior. Это означает, что все может случиться. Все.

Таким образом, вы можете получить старое значение, мусор или сег-ошибку. Вы не можете зависеть от чего-либо.

Если вы хотите проверить границы, используйте функцию at() member вместо operator []. Он выдает исключение вместо вызова Undefined Behavior.

Ответ 2

Вы не имеете права получить ошибку сегментации. В этом случае ошибка сегментации даже не является частью С++. Ваша программа удаляет все элементы из вектора, и вы незаконно получаете доступ к контейнеру за пределами границ. Это поведение undefined, что означает, что все может случиться. И действительно, что-то случилось.

Ответ 3

Из cppreference:

void clear();

Удаляет все элементы из контейнера. Недействительные ссылки, указатели или итераторы, ссылающиеся на содержащиеся элементы. Может привести к аннулированию любых итераторов в прошлом. Многие реализации не будут освобождать выделенную память после вызова clear(), фактически оставляя емкость вектора неизменной.

Итак, причина в том, что нет очевидной проблемы, потому что у вектора все еще есть память, доступная в магазине. Конечно, это просто конкретная реализация, но не ошибка. Кроме того, как указывают другие ответы, ваша программа также имеет Undefined Поведение для доступа к очищенному содержимому в первую очередь, поэтому технически все может случиться.

Ответ 4

operator[] эффективен, но идет по цене: он не выполняет проверку границ.

Есть более безопасный, но эффективный способ доступа к вектору, например, итераторам и т.д.

Если вам нужен вектор для произвольного доступа (т.е. не всегда последовательный), будьте очень осторожны с тем, как вы пишете свои программы, или используйте менее эффективный at(), который в тех же условиях создал бы исключение.

Ответ 5

Представьте, что вы богаты (возможно, вы есть, или вы не... вообще)!

Поскольку вы богаты, вы покупаете кусок земли на Муреа (Наветренные острова, Французская Полинезия). Вы очень уверены, что это хорошая собственность, поэтому вы строите виллу на этом острове, и вы там живете. В вашей вилле есть бассейн, теннисный корт, большой гараж и еще более приятный материал.

Через некоторое время вы покидаете Мурею, так как считаете, что это становится очень скучным. Много видов спорта, но мало людей. Вы продаете свою землю и виллу и решаете переехать где-то еще.

Если вы вернетесь через некоторое время, вы можете столкнуться с множеством разных вещей, но вы не можете быть уверены в том, что даже один из них.

  • Ваша вилла может исчезнуть, ее заменит клубный отель.
  • Ваша вилла может быть там.
  • Остров может быть затоплен.
  • ...

Кто знает? Несмотря на то, что вилла больше не может принадлежать вам, вы даже можете прыгать в бассейн или снова играть в теннис. Там может быть еще одна вилла рядом с ней, где вы можете искупаться в еще большем пуле, и никто не отвлекает вас.

У вас нет гарантии того, что вы хотите узнать, если вы вернетесь снова и что то же самое с вашим вектором, который содержит три указателя в реализациях, на которые я смотрел: (Имена могут быть разными, но функция в основном одинакова.)

  • begin указывает на начало выделенной памяти (т.е. X)
  • end, который указывает на конец выделенной памяти +1 (т.е. begin + 4)
  • last, который указывает на последний элемент в контейнере +1 (т.е. begin + 4)

Вызывая очистку, контейнер может уничтожить все элементы и reset last = begin;. Функция size() скорее всего будет return last-begin;, и вы увидите размер контейнера 0. Тем не менее, begin все еще может быть действительным, и все еще может быть выделена память (end может быть begin+4). Вы еще можете наблюдать за значениями, установленными перед clear().

std::vector<int> a(4);
a[2] = 12;
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;
a.clear();
cout << "a cap " << a.capacity() << ", ptr is " << a.data() << ", val 2 is " << a[2] << endl;

Печать

колпачок 4, ptr - 00746570, val 2 - 12
колпачок 4, ptr равен 00746570, val 2 составляет 12

Почему бы вам не заметить какие-либо ошибки? Это связано с тем, что std::vector<T>::operator[] не выполняет никаких проверок вне границ (в отличие от std::vector<T>::at()). Поскольку С++ не содержит "segfaults", ваша программа работает правильно.

Примечание. В MSVC 2012 operator[] выполняется граничная проверка, если она скомпилирована в режиме отладки.

Добро пожаловать в страну поведения undefined! Вещи могут или не могут произойти. Вы, вероятно, даже не можете описать одно обстоятельство. Вы можете рискнуть и быть достаточно смелыми, чтобы взглянуть на него, но это, вероятно, не способ создания надежного кода.

Ответ 6

вы можете получить seg-ошибку, но это не так, потому что доступ к элементам вне диапазона вектора с operator[] после clear(), который вызывается ранее, - это просто поведение undefined. С вашего поста похоже, что вы хотите попробовать, если элементы уничтожены, поэтому для этой цели вы можете использовать публичную функцию at:

Функция автоматически проверяет, находится ли n в пределах допустимые элементы в векторе, выбрасывая исключение out_of_range, если оно (т.е. если n больше или равно его размеру). Это в контраст с оператором-членом [], который не проверяет границы.

кроме того, после clear():

Все итераторы, указатели и ссылки, связанные с этим контейнером, аннулированной.

http://www.cplusplus.com/reference/vector/vector/at/

Ответ 7

Если вы используете

temp1.at(2) = 343.5;

вместо

temp1[2] = 343.5;

вы найдете проблему. Рекомендуется использовать функцию at(), а operator[] не проверяет границу. Вы можете избежать ошибки, не зная реализации вектора STL.

Кстати, я запускаю свой код в моем Ubuntu (12.04), получается, как вы говорите. Однако в Win7 он сообщил "Ошибка подтверждения".

Хорошо, это напоминает мне тип stringstream. Если определить предложение

stringstream str;
str << "3456";

Если REUSE str, мне сказали сделать это

str.str("");
str.clear();

вместо того, чтобы просто использовать предложение

str.clear();

И я попробовал resize(0) в Ubuntu, он оказался бесполезным.

Ответ 8

Да, это нормально. clear() не гарантирует перераспределение. Попробуйте использовать resize() после clear().

Ответ 9

попытайтесь получить доступ к элементам sup, чем 4, которые вы используете для конструктора, возможно, вы получите ошибку сегментации Другая идея cplusplus.com:

Очистить содержимое

Удаляет все элементы из вектора (которые уничтожаются), оставляя контейнер размером 0.

Перераспределение не гарантируется, и векторная емкость не может быть изменена из-за вызова этой функции. Типичная альтернатива, которая приводит к перераспределению, заключается в использовании swap:

вектор() подкачки (х).//очистка x перераспределение

Ответ 10

Одним из важных дополнений к ответам до сих пор: если класс, который был проинформирован вектором, предоставляет деструктор, он будет вызван на очистку (и на resize(0) тоже).

Попробуйте следующее:

struct C
{
    char* data;
    C()           { data = strdup("hello"); }
    C(C const& c) { data = strdup(c.data); }
    ~C()          { delete data; data = 0; };
};
int main(int argc, char** argv)
{
    std::vector<C> v;
    v.push_back(C());
    puts(v[0].data);
    v.clear();
    char* data = v[0].data; // likely to survive
    puts(data);             // likely to crash
    return 0;
}

Эта программа, скорее всего, выйдет из строя с ошибкой сегментации, но (скорее всего) не на char* data = v[0].data;, а в строке puts(data); (используйте отладчик, чтобы видеть).

Типичные реализации вектора оставляют сохраненную память неповрежденной и оставляют ее как сразу после вызова деструкторов (однако, нет гарантии - помните, это поведение undefined!). Последнее, что было сделано, - установить данные экземпляра C на nullptr, и, хотя это недействительно в силе С++/vector, память все еще существует, поэтому можно получить доступ к ней (незаконно) без ошибки сегментации. Это произойдет при разыменовании указателя char* data в puts, как null...