2013-06-02 2 views
1

Например:Является ли это законным использование цикла for?

vector<int> something; 
//imagine i add some elements to the vector here 

int* pointy; 
for (int i = 0; i < something.size(); pointy = &something[i++]) { 
     //do some work with pointy 
} 

, кажется, работает и экономит мне линию, но есть ли опасность странные ошибки выскакивают вниз линии из-за этого?

+2

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

+0

Почему вы не используете итераторы, предоставленные 'std :: vector' ?? –

+0

Нет. Все в порядке.Третий аргумент ожидает действительного выражения. И это получает. – karthikr

ответ

13

Это может быть не законным, потому что pointy не назначен на первой итерации. Если во время начальной итерации цикл не разыгрывает pointy, это может быть ОК, но невозможно определить, не увидев тело цикла.

Поскольку вы используете std::vector, использование итераторов сэкономит вам еще одну строку, потому что вам не нужно будет объявлять pointy. Вы могли бы определить смещение без i путем вычитания something.begin() из текущего итератора:

for (vector<int>::iterator iter = something.begin() ; iter != something.end() ; ++iter) { 
    cout << "Item at index " << (iter - something.begin()) << " is " << *iter << endl; 
} 
+0

На самом деле я этого не заметил. Yay обфускация! – Dave

6

Да, это опасно, as dasblinkenlight pointed out. Но есть более простой способ устранить такие проблемы.

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

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

В C++ 11, рассмотреть вопрос об использовании диапазона на основе for цикл:

for (int& p : something) 
{ 
    // ... 
} 

В C++ 03, рекомендуется использовать std::for_each() или классическую петлю на основе итераторы:

for (std::vector<int>::iterator i = something.begin(); i != something.end(); ++i) 
{ 
    // use *i to refer to the current pointy 
    // use (i - something.begin()) to get its index 
    // ... 
} 
+1

Цикл, основанный на диапазоне, определенно является способом перехода на C++ 11. Приятно, что вы также упоминали алгоритм 'std :: for_each' для C++ 03, но без lambdas он действительно производит обфускационный код, на мой взгляд, если не использовать многоразовый функтор. – leemes

+1

@leemes: True, 'std :: for_each' без лямбда - это болезненная вещь. С другой стороны, первые 8 символов понятны о цели цикла, в то время как в случае ручного цикла вам действительно нужно пойти и заглянуть внутрь цикла, чтобы убедиться, что 'i' не управляется, и что то, что он делает, - это перебирать каждый элемент. Но да, я согласен, что основанный на функторе подход раздражает. Я, вероятно, чаще бывал для ручного цикла, чем не в C++ 03. –

+0

Я полностью согласен с «фиксированной» семантикой при использовании алгоритма std, который является одной из их сильных сторон. – leemes

0

Является ли это безопасным действительно зависит от того, что вы после. pointy будет указателем на элемент в векторе. Это означает, что если вы измените значение pointy или что оно указывает на более конкретное, вы фактически меняете содержимое вектора для этого конкретного элемента.

+1

Так в чем проблема? – leemes

1

Очень опасно, потому что i не unsigned. Он может взорваться в некоторых редких случаях. :)

+0

Как он может взорваться? – jogojapan

+0

Если 'something.size()> INT_MAX' (возможно), это будет бесконечный цикл. Если компьютер остается в бесконечном цикле в течение длительного времени, он может взорваться. – cubuspl42

+0

Если 'something.size()> INT_MAX', вы, строго говоря, получите неопределенное поведение. Но решение этого заключается в использовании 'std :: vector :: size_type' (что не обязательно совпадает с' unsigned'). Если вы хотите быть очень строгим. – jogojapan

0

Что касается меня, я люблю обращаться зЬй :: вектор, как это для больших объектов:

std::vector<int*> mynumbers; 
//add elements here like this: 
int somenumber = 5; 
mynumbers.push_back(&somenumber); 

for(int i=0;i<elemnts.size();i++) 
{ 
    cout << "Element Nr. " << i << ": " << *elements.at(i) << endl; 
    //modify like this: 
    *elements.at(i) = 0; 
} 

Указатель вместо самой переменной, так как станд :: вектор обрабатывает указатели быстрее, чем крупные объекты сам, но для междунар, это на самом деле не имеет большого значения, так что вы можете сделать это следующим образом:

std::vector<int> mynumbers; 
mynumbers.push_back(5); 

int* pointy 
for(int i=0;i<elemnts.size();i++) 
{ 
    pointy = &elements.at(i); 
} 

Работает отлично подходит для меня!

+0

Что такое мотивация для перехода от 'vector ' to 'vector '? – jogojapan

+0

Для int это не имеет особого смысла, но когда вы пытаетесь управлять массивом больших объектов с большим количеством данных, std :: vector будет обрабатывать некоторые указатели быстрее, чем сами объекты. Я добавлю это к своему ответу ... – theCNG27

+1

Но вы понимаете, что может быть много проблем с нажатием адресов локальных переменных на вектор, правильно? Как правило, вы в конечном итоге распределяете объекты динамически, и это огромные накладные расходы. В любом случае, как это связано с вопросом? Речь идет о сложном for-statement. – jogojapan