수색…
소개
루프 문은 조건이 충족 될 때까지 반복적으로 명령문 그룹을 실행합니다. C ++에는 3 가지 유형의 원시 루프가 있습니다 : for, while 및 do ... while.
통사론
- 동안 ( 조건 ) 성명서 ;
- while 문 ( 표현 );
- for ( for-init-statement ; 조건 ; 표현 ) 구문 ;
- for ( 범위 내 선언 : for-range-initializer ) 문 ;
- 휴식;
- 계속;
비고
algorithm
호출은 일반적으로 수작업으로 작성된 루프보다 바람직합니다.
알고리즘이 이미 (또는 매우 비슷한) 것을 원한다면, 알고리즘 호출은 더 명확하고, 종종 더 효율적이고 오류가 발생하기 쉽습니다.
상당히 간단한 작업을하는 루프가 필요한 경우 (그러나 알고리즘을 사용하는 경우 바인더와 어댑터가 혼란 스럽기 때문에) 루프를 작성하십시오.
범위 기반
for
루프는 숫자 인덱스를 사용하거나 이터레이터에 직접 액세스하지 않고 반복기 기반 범위의 요소를 반복하는 데 사용할 수 있습니다.
vector<float> v = {0.4f, 12.5f, 16.234f};
for(auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
이것은의 모든 요소를 반복합니다 v
로, val
현재의 요소의 값을 받고. 다음 진술 :
for (for-range-declaration : for-range-initializer ) statement
다음과 같습니다.
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
이 변경 사항은 C ++ 20의 Ranges TS에 대한 계획된 지원을 위해 도입되었습니다.
이 경우 루프는 다음과 같습니다.
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
auto val
은 범위에 저장된 값의 복사본이 될 값 유형을 선언합니다 (우리는 iterator에서 복사 초기화됩니다). 범위에 저장된 값이 복사하는 데 비용이 많이 든다면 const auto &val
을 사용할 수 있습니다. auto
을 사용할 필요도 없습니다. 범위의 값 유형에서 암시 적으로 변환 할 수있는 한 적절한 유형 이름을 사용할 수 있습니다.
iterator에 대한 액세스가 필요하다면 range-based for은 당신을 도울 수 없다.
참조하려는 경우 다음과 같이 할 수 있습니다.
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
const
컨테이너가있는 경우 const
참조를 반복 할 수 있습니다.
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
시퀀스 반복자가 프록시 객체를 반환하고 비 const
방식으로 객체를 조작해야하는 경우 전달 참조를 사용합니다. 참고 : 코드 독자에게 혼동을 줄 수 있습니다.
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
range 기반의 for
제공되는 "range"유형은 다음 중 하나 일 수 있습니다.
언어 배열 :
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; }
동적 배열을 할당하는 것은 중요하지 않습니다.
float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
멤버 함수
begin()
과end()
가있는 모든 유형. 반복자를 해당 유형의 요소로 반환합니다. 표준 라이브러리 컨테이너는 한정되어 있지만 사용자 정의 유형도 사용할 수 있습니다.struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
유형에 따라 인수 종속 조회를 통해 찾을 수있는 비 멤버
begin(type)
및end(type)
함수가있는 모든type
입니다. 이는 클래스 유형 자체를 수정하지 않고도 범위 유형을 만드는 데 유용합니다.namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
For 루프
for
루프는 루프 condition
이 참인 동안 loop body
명령문을 실행합니다. 루프 initialization statement
은 정확히 한 번 실행됩니다. 매 사이클마다 iteration execution
부분이 실행됩니다.
for
루프는 다음과 같이 정의됩니다.
for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
// body of the loop
}
자리 표시 자 설명
-
initialization statement
:이 문은for
루프의 시작 부분에서 한 번만 실행됩니다.int i = 0, a = 2, b = 3
과 같이 한 유형의 여러 변수 선언을 입력 할 수 있습니다. 이러한 변수는 루프 범위에서만 유효합니다. 같은 이름의 루프 앞에 정의 된 변수는 루프 실행 중에 숨겨집니다. -
condition
:이 문은 각 루프 본문 실행보다 먼저 평가되고,false
평가되면 루프를 중단합니다. -
iteration execution
:이 문은for
루프가 본문 에서 중단되지 않는 한 (break
,goto
,return
또는 throw되는 예외에 의해) 다음 조건 평가에 앞서 루프 본문 뒤에 실행됩니다.a++, b+=10, c=b+a
와 같이iteration execution
부분에 여러 명령문을 입력 할 수 있습니다.
while
돌이로 재 작성된 for
돌이의 대략적인 등가물은 다음과 같습니다.
/*initialization*/
while (/*condition*/)
{
// body of the loop; using 'continue' will skip to increment part below
/*iteration execution*/
}
for
루프를 사용하는 가장 일반적인 경우는 특정 횟수만큼 명령문을 실행하는 것입니다. 예를 들어, 다음을 고려하십시오.
for(int i = 0; i < 10; i++) {
std::cout << i << std::endl;
}
유효한 루프 또한 다음과 같습니다.
for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
std::cout << a << " " << b << " " << c << std::endl;
}
루프 전에 선언 된 변수를 숨기는 예제는 다음과 같습니다.
int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99
그러나 이미 선언 된 변수를 사용하고 숨기지 않으려면 선언 부분을 생략하십시오.
int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10
노트:
- 초기화 및 증가 명령.은 조건 명령문과 무관 한 조작을 수행 할 수 있습니다. 그러나 가독성을 위해 루프에 직접 관련된 작업 만 수행하는 것이 좋습니다.
- 초기화 문에서 선언 된 변수는
for
루프의 범위 내에서만 볼 수 있으며 루프가 종료되면 해제됩니다. -
initialization statement
에서 선언 된 변수는 루프 중에 수정 될 수 있으며condition
에서 확인 된 변수도 수정할 수 있습니다.
0에서 10까지 카운트하는 루프의 예 :
for (int counter = 0; counter <= 10; ++counter)
{
std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)
코드 조각에 대한 설명 :
-
int counter = 0
은 변수counter
를int counter = 0
초기화합니다 (이 변수는for
루프에서만 사용할 수 있습니다). -
counter <= 10
은counter
가 10보다 작거나 같은지 여부를 확인하는 부울 조건입니다.true
경우 루프가 실행됩니다.false
이면 루프가 끝납니다. -
++counter
는 다음 조건 검사보다counter
의 값을 1 씩 증가시키는 증가 연산입니다.
모든 문을 비워두면 무한 루프를 만들 수 있습니다.
// infinite loop
for (;;)
std::cout << "Never ending!\n";
while
루프에 상응하는 내용은 다음과 같습니다.
// infinite loop
while (true)
std::cout << "Never ending!\n";
그러나 break
, goto
또는 return
문을 사용하거나 예외를 throw하여 여전히 무한 루프를 남길 수 있습니다.
<algorithm>
헤더를 사용하지 않고 STL 컬렉션 (예 : vector
)의 모든 요소를 반복하는 다음 일반적인 예는 다음과 같습니다.
std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
std::cout << *it << std::endl;
}
While 루프
while
루프는 주어진 조건이 false
평가 될 때까지 반복적으로 명령문을 실행합니다. 이 제어 명령.은 사전에 코드 블록을 몇 번 실행해야하는지 알 수 없을 때 사용됩니다.
예를 들어, 0부터 9까지의 모든 숫자를 인쇄하려면 다음 코드를 사용할 수 있습니다.
int i = 0;
while (i < 10)
{
std::cout << i << " ";
++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17부터 처음 두 문장을 결합 할 수 있습니다.
while (int i = 0; i < 10)
//... The rest is the same
무한 루프를 만들려면 다음 구문을 사용할 수 있습니다.
while (true)
{
// Do something forever (however, you can exit the loop by calling 'break'
}
while
루프의 또 다른 변종, 즉 do...while
구문이 있습니다. 자세한 정보는 do-while 루프 예제 를 참조하십시오.
조건 변수 선언
for
및 while
루프의 조건에서 객체를 선언 할 수도 있습니다. 이 객체는 루프의 끝까지 범위에있는 것으로 간주되며 루프의 각 반복을 통해 지속됩니다.
for (int i = 0; i < 5; ++i) {
do_something(i);
}
// i is no longer in scope.
for (auto& a : some_container) {
a.do_something();
}
// a is no longer in scope.
while(std::shared_ptr<Object> p = get_object()) {
p->do_something();
}
// p is no longer in scope.
그러나 do...while
루프를 사용하여 동일한 작업을 수행 할 수 없습니다. 루프 앞에서 변수를 선언하고 루프가 끝난 후에 변수를 범위 밖으로 벗어나게하려면 변수와 루프를 모두 로컬 범위로 묶습니다 (선택 사항).
//This doesn't compile
do {
s = do_something();
} while (short s > 0);
// Good
short s;
do {
s = do_something();
} while (s > 0);
이것은 계산서 부 때문이다 do...while
루프 (루프의 본체) 발현 부 (전과 평가 while
)에 도달하고, 따라서, 식 임의 선언은 첫 반복 동안 보이지 않는 것 고리.
Do-while 루프
조건이 아니라 시작에, 각주기의 끝에서 체크를 제외 할 - while 루프, while 루프와 매우 유사합니다. 따라서 루프는 적어도 한 번 이상 실행되도록 보장됩니다.
다음 코드는 0
을 출력합니다. 첫 번째 반복이 끝날 때 조건이 false
로 평가됩니다.
int i =0;
do
{
std::cout << i;
++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console
참고 : while(condition);
의 끝에 세미콜론을 잊지 마십시오 while(condition);
이것은 do-while 구조에서 필요합니다.
do-while 루프와 달리 첫 번째 반복의 시작 부분에서 조건이 false
로 평가되기 때문에 다음은 아무 것도 인쇄하지 않습니다.
int i =0;
while (i < 0)
{
std::cout << i;
++i; // Increment counter
}
std::cout << std::endl; // End of line; nothing is printed to the console
주 : break
, goto
또는 return
문을 사용하면 조건이 false가되지 않고 while 루프를 종료 할 수 있습니다.
int i = 0;
do
{
std::cout << i;
++i; // Increment counter
if (i > 5)
{
break;
}
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console
사소한 do-while 루프는 종종 자신의 범위가 필요한 매크로를 작성하는 데 사용됩니다 (이 경우 매크로 정의에서 후행 세미콜론은 생략되어 사용자가 제공해야합니다).
#define BAD_MACRO(x) f1(x); f2(x); f3(x);
// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);
#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)
// All calls are protected here
if (cond) GOOD_MACRO(var);
루프 제어문 : 중단 및 계속
루프 제어 문은 정상적인 순서에서 실행 흐름을 변경하는 데 사용됩니다. 실행이 범위를 벗어나면 해당 범위에서 작성된 모든 자동 개체가 삭제됩니다. break
및 continue
은 루프 제어 문입니다.
break
문은 더 이상 고려하지 않고 루프를 종료합니다.
for (int i = 0; i < 10; i++)
{
if (i == 4)
break; // this will immediately exit our loop
std::cout << i << '\n';
}
위의 코드가 출력됩니다 :
1
2
3
continue
문은 루프를 즉시 종료하지 않고 루프 본문의 나머지 부분을 건너 뛰고 루프의 상단으로 이동합니다 (조건 확인 포함).
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0) // evaluates to true if i is even
continue; // this will immediately go back to the start of the loop
/* the next line will only be reached if the above "continue" statement
does not execute */
std::cout << i << " is an odd number\n";
}
위의 코드가 출력됩니다 :
1 is an odd number
3 is an odd number
5 is an odd number
이러한 제어 흐름의 변화는 인간이 쉽게 이해할 수없는 경우가 있기 때문에 break
하고 continue
사용하지 마십시오. 보다 직접적인 구현은 일반적으로 읽고 이해하기가 더 쉽습니다. 예를 들어 위의 break
가진 첫 번째 for
루프는 다음과 같이 재 작성 될 수 있습니다.
for (int i = 0; i < 4; i++)
{
std::cout << i << '\n';
}
continue
가있는 두 번째 예제는 다음과 같이 다시 작성 될 수 있습니다.
for (int i = 0; i < 6; i++)
{
if (i % 2 != 0) {
std::cout << i << " is an odd number\n";
}
}
범위 미만의 범위
범위 기반 루프를 사용하면 범위 기반 루프를위한 프록시 객체를 생성하여 지정된 컨테이너 또는 다른 범위의 하위 부분을 반복 할 수 있습니다.
template<class Iterator, class Sentinel=Iterator>
struct range_t {
Iterator b;
Sentinel e;
Iterator begin() const { return b; }
Sentinel end() const { return e; }
bool empty() const { return begin()==end(); }
range_t without_front( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {std::next(b, count), e};
}
range_t without_back( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {b, std::prev(e, count)};
}
};
template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
using std::begin; using std::end;
return range(begin(r),end(r));
}
template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}
이제 우리가 할 수있는 일은 :
std::vector<int> v = {1,2,3,4};
for (auto i : except_first(v))
std::cout << i << '\n';
인쇄
2
3
4
for
루프의 for(:range_expression)
부분에서 생성 된 중간 객체는 for
루프가 시작될 때까지 만료됩니다.