범위 기반 for
범위 기반 for 루프는 배열이나 컨테이너 같이 데이터의 연속을 나타내는 객체의 모든 멤버를 순환하는 구문입니다.
이 기능은 C++11부터 도입되었습니다.
선언 방식은 다음과 같습니다.
for ( 데이터_타입 변수명 : 배열 or 컨테이너 등 순환가능한 객체 ){}
범위 기반 for 구문의 대상은 배열이나 컨테이너뿐 아니라, 데이터가 연속되어 있는 순환가능한 객체라면 대상이 될 수 있습니다.
예를 들어, 문자열 안에 있는 모든 문자를 출력하는데도 이 구문을 사용할 수 있습니다.
그리고, "데이터_타입"은 순환가능한 객체가 가진 멤버의 데이터 타입을 말합니다.
이 구문의 장점은 기존의 for 구문보다 알아보기 쉽고 짧은 코드를 만들 수 있다는 점입니다.
하지만 기존의 for 구문은 iterator를 통한 배열이나 컨테이너의 개별적인 멤버에 접근이 가능했지만, 범위 기반 for 구문은 멤버에 선택적인 접근을 할 수는 없습니다.
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0; i < 9; i++){
int sum = arr[i] + arr[i+1]; // 인덱스로 배열의 멤버에 접근
cout << sum << " ";
}
cout << endl;
for( int v : arr){
cout << v << " "; // 멤버의 값에 바로 접근
}
return 0;
}
예를 들면, 배열의 경우 인덱스를 통해 개별의 멤버의 값에 접근할 수 있습니다.
그러나, 범위 기반 for 구문에선 바로 객체에 접근하는 방식을 사용합니다.
구체적인 코드를 보면 사용법을 쉽게 알 수 있습니다.
배열의 모든 멤버를 출력하는 코드를 다음과 같이 바꿀 수 있습니다.
#include <iostream>
using namespace std;
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0; i < 10; i++){ // 기존의 for 루프
cout << arr[i] << " ";
}
cout << "\n";
for(int v : arr){ // 범위 기반 for 루프
cout << v << " ";
}
return 0;
}
std::vector의 모든 멤버를 출력하는 코드는 다음과 같이 변경할 수 있습니다.
int main(){
vector<int> vec = {1,2,3,4,5,6,7,8,9,10};;
// 기존의 for 루프
for(vector<int>::iterator it = vec.begin(); it != vec.end(); it++){
cout << *it << " ";
}
cout << "\n";
for(int v : vec){ // 범위 기반 for 루프
cout << v << " ";
}
return 0;
}
문자열의 모든 문자를 출력하기 위해서 사용할 수도 있습니다.
#include <iostream>
#include <string>
using namespace std;
int main(){
string str = "This is a string";
for(char c : str){
cout << c << " ";
}
return 0;
}
범위 기반 for의 데이터 타입
범위 기반 for 구문의 데이터 타입은 참조(reference)와 const 키워드를 사용할 수 있습니다.
아래 예문은 클래스를 멤버로 가지고 있는 배열을 순환하는 코드입니다.
class CMyPoint{
public:
int x, y;
CMyPoint() : x(0), y(0) {};
};
int main(){
CMyPoint ptArr[10];
for(CMyPoint pt : ptArr){ // 범위 기반 for
cout << pt.x << " , " << pt.y << "\n";
}
return 0;
}
여기서, 위와 같이 데이터 타입을 CMyPoint로 하면 함수가 인자로 값을 전달하는 것과 같이 pt 변수는 ptArr의 멤버를 복사하게 됩니다.
이때, 데이터가 복사되는 것을 막기 위해서 참조 테이터 타입을 사용할 수 있습니다.
for(CMyPoint& pt : ptArr){ // 참조 데이터 타입
cout << pt.x << " , " << pt.y << "\n";
}
그리고, 참조 데이터 타입의 변수는 참조하고 있는 데이터의 값을 변경할 수 있습니다.
이러한 원본의 데이터 변경을 막기 위해서 const 키워드를 사용할 수 있습니다.
for(const CMyPoint& pt : ptArr){ // const 참조 데이터 타입
cout << pt.x << " , " << pt.y << "\n";
pt.x = 10; // error !!
}
또한, auto 키워드도 사용할 수 있습니다.
auto는 컴파일러가 선언된 변수 또는 변수의 초기화 식을 사용하여 해당 형식을 추론하도록 지시하는 키워드입니다.
for(const auto& pt : ptArr){ // const auto 데이터 타입
cout << pt.x << " , " << pt.y << "\n";
}
위의 auto 키워드는 CMyPoint 타입으로 처리됩니다.
범위 기반 for 구문으로 std::map 데이터 순환
map 컨테이너의 데이터를 순환하기 위해 범위 기반 for 구문을 사용하면 다음과 같습니다.
#include <iostream>
#include <map>
using namespace std;
int main(){
map<int, int> myMap{ {1,2}, {2,2}, {3,4} };
for( pair<const int, int>& i : myMap){
cout << i.first << " " << i.second << "\n"; // first, second를 사용
}
}
C++17 버전 이후에는, 위의 구문을 아래와 같이 변경할 수 있습니다.
int main(){
map<int, int> myMap{ {1,2}, {2,2}, {3,4} };
for( auto& [key, value] : myMap){
cout << key << " " << value << "\n"; // key, value 사용
}
return 0;
}
pair의 first, second 대신 key와 value 이름으로 직접 데이터에 접근할 수 있습니다.
이 글과 관련 있는 글들
범위 기반 for를 사용하기 위한 사용자 타입의 필요조건
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 가상 함수( Virtual Function )를 사용하는 이유 (0) | 2024.05.23 |
---|---|
[C++] 클래스의 멤버 함수 포인터 (member function pointer) (0) | 2024.05.16 |
[C++] 복사 생성자 (Copy Constructor)와 복사 생략 (0) | 2024.05.10 |
[C++] 클래스의 초기화 리스트 (Initializer List)를 사용하는 이유 (0) | 2024.05.07 |
[C++] const 포인터, const 멤버 함수 및 const 클래스 (0) | 2024.05.04 |