<개인공부>/[C++]

[C++] range based for, 범위기반 for 반복문에 대해서.

BlockDMask 2019. 3. 25. 01:19
반응형

안녕하십니까. BlockDMask입니다.

오늘 공부할 내용은 C++11에 추가된 범위기반 반복문 range based for문 입니다. 

혁명이죠. 놀랍죠. 하지만 범위기반 for문이 완전히 for문을 대체하지 못합니다. why? 왜때문이죠?

그럼 살펴보겠습니다.

<목차>

1. C++ range based for문 이란? (기본편 - 값복사)

2. C++ range based for문 예제 1 (순회)

3. C++ range based for문 예제 2 (for와 range based for의 차이)

5. C++ range based for문 이란? (심화편 - reference, const reference

5. C++ range based for문 reference 예제

6. C++ range based for문 const reference 예제


1. C++ range based for문 이란? (기본편 - 값복사)


▼ C++ 범위기반 for문이란?

기존의 for 반복문과 달리, 시작과 끝점을 알려주지 않아도 알아서 처음부터 끝까지 순회를 해주는 반복문 입니다.

C++11에서부터 사용을 할 수 있습니다.

C# 에서의 foreach와 같다고 생각하면 됩니다. [바로가기]


▼ C++ 범위기반 for문 사용법

for (데이터 타입 elem : 데이터 리스트)

{

당신이 하고싶은 그 모든것들. elem을 이용하면 됩니다.

}

따로 키워드가 있는 것은 아닙니다. for( --- ) 이 괄호 안에를 자세히 살펴보아야 합니다.

데이터 타입 : 데이터 리스트에서 부터 하나씩 받아올 데이터의 타입을 말합니다. 그 데이터는 변수 elem에 들어가게 됩니다.

변수 이름 elem : 데이터를 가지고 있는 변수 이름 입니다.

데이터 리스트 : 배열, vector와 같은 순회가 가능한 데이터 리스트 입니다. 데이터 리스트 앞에 꼭 : " (콜론)을 붙여줘야 합니다.


for문과 범위기반 for문의 차이점.

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for(int i=0; i<10; ++i)

{    

cout << arr[i] << endl;

}

기존의 for문은 이런식으로 했었습니다.


for(int elem : arr)

{

cout << elem << endl;

}

범위기반 for문은 이런식으로 순회하면됩니다.


이렇게 좋아보이는 base ranged for문이 완전히 for 반복문을 대체하지는 못합니다. 

왜, why, 왜때문일까요? (그것의 이유는 60초 뒤에)


1. 범위기반 for문에서는 index 정보가 존재하지 않습니다.

index 를 나타내는 아무런 정보가 없기 때문입니다.

기존의 for문에서는 위 예제에서 처럼 index를 나태는 " i " 가 존재하는데 범위기반 for문에는 존재하지 않습니다. 오직 elem이라는 값만 존재할 뿐입니다. index로 구분할 수 있는 여러가지 조절이 힘들다 단점이 존재합니다. (할 수 있습니다. 코드가 더러워질뿐)


2. 범위기반 for 배열의 요소를 변경할 수 없습니다. (변경 가능한것은 아래 4번 심화편을 봐주세요.)

매 반복문이 돌때마다 int elem : arr 을 통해서 하는 일은 아래와 같습니다.

elem = arr[0];

elem = arr[1];

이런식으로 배열의 요소들이 elem 이라는 변수에 복사됩니다.

그래서 배열의 요소를 내부에서 바꾸려고 elem = 1; 시도를 해도. 복사된 값 이기 때문에 arr[0] 의 값이 바뀌거나 하지않습니다.

그래서 range based for문 내부에서는 배열의 요소를 변경할수 없습니다.


2. C++ range based for문 예제 1 (순회)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//C++ range based for example.
//BlockDMask.
#include<iostream>
#include<vector>
using namespace std;
int main(void)
{
    cout << "ex1) 일반적인 배열에서 범위기반 for문 사용법" << endl;
    cout << "for (int elem : arr)" << endl;
    int arr[10= { 12345678910 };
    for (int elem : arr)
    {
        cout << elem << " ";
    }
 
    cout << endl << endl;
 
    cout << "ex2) vector같은 std container에서 범위기반 for문 사용법" << endl;
    cout << "for (int elem : v)" << endl;
    vector<int> v;
    v.push_back(1); v.push_back(2); v.push_back(3);
    v.push_back(4); v.push_back(5); v.push_back(6);
    v.push_back(7); v.push_back(8); v.push_back(9); v.push_back(10);
    for (int elem : v)
    {
        cout << elem << " ";
    }
    cout << "\niterator를 사용하지 않아도 된다는 것이 좋습니다." << endl;
       
    cout << endl << endl;
    system("pause");
    return 0;
}
cs


▲ 범위기반 for문 사용법 1

일반적인 순회예제 입니다. ex2) 에서 원래 for문을 이용했다면 

1
for(vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
cs

이런방식으로 iterator를 이용해서 for문을 사용했어야 했습니다.

하지만 범위기반 for문에서는 이런 번거로운 작업이 없습니다.


3. C++ range based for문 예제 2 (for와 range based for의 차이)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//C++ range based for example.
//BlockDMask.
#include<iostream>
#include<vector>
using namespace std;
int main(void)
{
    vector<int> v;
    v.push_back(1); v.push_back(2); v.push_back(3);
    v.push_back(4); v.push_back(5); v.push_back(6);
    v.push_back(7); v.push_back(8); v.push_back(9); v.push_back(10);
    
    cout << "ex1) 기존의 반복문을 사용했을때." << endl;
    cout << "for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)" << endl;
    for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
    {
        cout << *iter << " ";
    }
 
    cout << endl << endl;
    cout << "ex2) 범위기반 반복문을 사용했을때." << endl;
    cout << "for (int elem : v)" << endl;
    for (int elem : v)
    {
        cout << elem << " ";
    }
 
    cout << endl << endl;
    cout << "ex3) 기존 반복문에서 원래 데이터 변경 확인" << endl;
    for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
    {
        *iter += 10;
        cout << *iter << " ";    
    }
    cout << endl;
    for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
    {
        cout << *iter << " ";
    }
 
    cout << endl << endl;
    cout << "ex4) 범위기반 반복문에서 원래 데이터 변경 확인" << endl;
    for (int elem : v)
    {
        elem += 100;
        cout << elem << " ";
    }
    
    cout << endl;
    for (int elem : v)
    {
        cout << elem << " ";
    }
 
    cout << endl << endl;
    system("pause");
    return 0;
}
cs


▲ 범위기반 for문 사용법 2

1,2번 예제 : 처음부터 끝까지 순회하는 것이라면 범위기반 for문이 훨씬 문장이 짧습니다. 

3,4번 예제 : 범위기반 for문의 변수 elem 값을 바꾼다 하더라도, 원래 데이터가 변경되지는 않습니다.


4. C++ range based for문 이란? (심화편 - reference, const reference)


우리는 위에서 기본적인 범위기반 for문을 배웠습니다. 하지만, 


int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for(int elem : arr)

{

cout << elem << endl;

}

이런식으로 위쪽 기본편에서 배운데로 하게 되면, arr 배열에 있는 인자를 elem 이라는 새로운 변수에 복사를 하게 됩니다.

매 반복문이 돌때마다 아래처럼 복사가 됩니다.

elem = arr[0];

elem = arr[1];

elem = arr[2];

복사를 하게되면 단점이 많습니다.

1. 복사를 했기 때문에 배열의 원래 값를 변경하지 못합니다.

2. 복사를 했기 때문에 복사비용이 발생합니다.


이러한 단점을 보완하기 위해서는 C++의 (참조자, reference)를 이용하면 됩니다.

(혹시 C++ 참조자 & (reference)에 대한 공부가 필요하다면 [바로가기])


▼ C++ range based for문을 reference로 사용하는 방법.

for(int& elem : arr)

{

elem += 1;    //arr[ ] 값도 변경됨.

cout << elem << endl;

}

for(데이터 타입& 변수이름 : 데이터리스트) 와 같이 데이터 타입 뒤에 레퍼런스(&)를 붙이는 방식으로 한다면, 

배열의 해당 인자의 값복사가 아닌 변수의 reference(=참조자, 레퍼런스)를 가지고 오게 됩니다.

이렇게 할때의 이점을 아래와 같습니다.

1. 복사비용이 들지 않아서 비용이 감소 합니다.

2. 배열의 원래 원소를 변경할 수 있습니다.


이렇게 되면 다 해결이 되어서 좋지만, 복사비용이 안드는 것은 좋은데 반복문 내부에서 변경이 일어나지 않아야 하는 경우도 존재하죠.

이런 경우에는 constreference를 같이 사용하면됩니다.


▼ C++ range based for문을 const와 reference로 사용하는 방법.

for(const int& elem : arr)

{

elem += 1;    // 불가능.

cout << elem << endl;

}

for(const 데이터 타입& 변수이름 : 데이터리스트) 이런방식으로 데이터 타입 앞에 const를 붙이면 값 변경이 불가능하게 됩니다.

그러면 복사비용이 들지 않고 배열의 값을 이용할 수 있고, 배열의 값을 변경하지 않는걸 보장할 수 있게 됩니다.


const & 를 사용할지, &를 사용할지 아니면 그냥 복사로 사용할지는 각 상황에 맞게 잘 판단해서 하면 됩니다.


5. C++ range based for문 reference 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//C++ range based for, reference example.
//BlockDMask.
#include<iostream>
#include<vector>
using namespace std;
int main(void)
{
    vector<int> v;
    v.push_back(1); v.push_back(2); v.push_back(3);
    v.push_back(4); v.push_back(5); v.push_back(6);
    v.push_back(7); v.push_back(8); v.push_back(9); v.push_back(10);
 
    cout << "ex1) 범위기반 반복문 reference를 이용한 데이터 변경" << endl;
    for (int& elem : v)
    {
        elem += 100;
        cout << elem << " ";
    }
 
    cout << endl;
    for (int& elem : v)
    {
        cout << elem << " ";
    } 
    
    cout << endl << endl;
    system("pause");
    return 0;
}
cs


▲ 범위기반 for문 reference 사용법

저 위에 for와 range based for문의 차이 예제에서와 달리, & (래퍼런스, 참조자)를 이용하니까 원본데이터가 바뀐것을 볼 수 있습니다.


6. C++ range based for문 const reference 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//C++ range based for, const reference example.
//BlockDMask.
#include<iostream>
#include<vector>
using namespace std;
int main(void)
{
    vector<int> v;
    v.push_back(1); v.push_back(2); v.push_back(3);
    v.push_back(4); v.push_back(5); v.push_back(6);
    v.push_back(7); v.push_back(8); v.push_back(9); 
    v.push_back(10);
 
    cout << "ex) 범위기반 반복문 const reference를 이용한 데이터 변경 불가능" << endl;
    for (const int& elem : v)
    {
        //elem += 100; 불가능.
        cout << elem << " ";
    }
 
    cout << endl;
    for (int& elem : v)
    {
        elem += 100//가능.
        cout << elem << " ";
    }
 
    cout << endl;
    for (const int& elem : v)
    {
        cout << elem << " ";
    }
 
    cout << endl << endl;
    system("pause");
    return 0;
}
cs


▲ 범위기반 for문 const reference 사용법

const reference 를 사용한 for문 내부에서는 데이터 값 변경이 불가능 한 것을 볼 수 있습니다.


감사합니다. 새로운 포스팅이 보고싶다면 "구독" 부탁드립니다.


반응형