Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:
null
. null
. null
, как если бы это был массив. null
, как если бы это был массив. null
как будто это было значение Throwable. Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null
.
Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
Обратите внимание, что обычно реализация вектора не будет использовать "интервал" в качестве типа индекса/размера. Таким образом, Ваш код по крайней мере вызовет предупреждения компилятора.
Итераторы увеличивают степень универсальности Вашего кода.
, Например:
typedef std::vector<int> Container ;
void doSomething(Container & p_aC)
{
for(Container::iterator it = p_aC.begin(), itEnd = p_aC.end(); it != itEnd; ++it)
{
int & i = *it ; // i is now a reference to the value iterated
// do something with "i"
}
}
Теперь, давайте предположим изменение вектора в список (потому что в случае, список теперь лучше). Только необходимо изменить объявление определения типа и перекомпилировать код.
Должен Вы использовать основанный на индексе код вместо этого, он должен был бы быть переписан.
итератор должен быть просмотрен как своего рода супер указатель. Это "указывает" на значение (или, в случае карт, к паре ключа/значения).
, Но это имеет методы для перемещения в следующий объект в контейнере. Или предыдущее. Некоторые контейнеры предлагают даже произвольный доступ (вектор и двухсторонняя очередь).
Большинство алгоритмов STL работает над итераторами или над диапазонами итераторов (снова из-за степени универсальности). Вы не будете в состоянии использовать индекс, здесь.
Используя итераторы позволяет Вашему коду быть агностиком о реализации Вашего контейнера. Если произвольный доступ для Вашего контейнера является дешевым, нет большого мудрого производительностью различия.
, Но в большом количестве случаев Вы не будете знать, имеет ли это место. Если Вы пробуете, действительно используют Ваш метод в связанном списке, например, с индексированием, контейнер оказывается перед необходимостью обходить список на каждом повторении для нахождения элемента.
Поэтому, если Вы не знаете наверняка, что произвольный доступ к Вашему контейнеру является дешевым, используйте итератор.
Если Вы используете итераторы в качестве аргументов Вашей функции, можно разъединить ее от типа используемого "контейнера". Например, Вы могли бы направить результаты функции подключиться с консоли к выводу, а не вектору (пример ниже). Этот прием может быть чрезвычайно мощным для сокращения связи между Вашими классами. Слабо связанные классы намного легче протестировать.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <typename InputIterator, typename OutputIterator>
void AddOne(InputIterator begin, InputIterator end, OutputIterator dest)
{
while (begin != end)
{
*dest = *begin + 1;
++dest;
++begin;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> data;
data.push_back(1);
data.push_back(2);
data.push_back(3);
// Compute intermediate results vector and dump to console
vector<int> results;
AddOne(data.begin(), data.end(), back_inserter(results));
copy(results.begin(), results.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// Compute results and send directly to console, no intermediate vector required
AddOne(data.begin(), data.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
В Вашем примере вызов к vecVector.size () менее эффективен, чем использование итератора. Итератор по существу инкапсулирует Вас от необходимости волноваться о размере контейнера, выполняемого с помощью итераций на. Кроме того, итератор не делает необходимый, должны войти в последовательный порядок. Это просто должно ответить на вызов .next любым способом, которым это считает целесообразным.
Ну, с одной стороны, вышеупомянутое больше не будет работать, если Вы превратите тот вектор в список.
Итераторы позволяют Вам создавать шаблоны функций, которые не должны знать тип контейнера, они продолжают работать. Можно даже сделать следующее:
#include <algorithm>
void printvalue(double s)
{
// Do something with s
}
int _tmain(int argc, _TCHAR* argv[])
{
double s[20] = {0};
std::for_each(s, s+20, printvalue);
return 0;
}
Поэтому стандартный указатель является также допустимым итератором для for_each.
Dave
Итератор является главным образом более высоким уровнем абстракции.
Ваш отрывок предполагает, что контейнер может быть индексирован. Это верно для std::vector<>
и некоторые другие контейнеры, например, необработанные массивы.
, Но std::set<>
испытывает недостаток в индексации полностью, и индексный оператор std::map<>
вставит любой аргумент, предоставляется ему в карту - не ожидаемое поведение в Вашем for
- цикл.
кроме того, проблемами производительности являются только проблемы, когда измерено и доказано так.