차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판 이전 판 다음 판 | 이전 판 | ||
activity:public:2021:cpp:210824 [2021/08/24 18:36:05] – [1.1 객체 배열이란?] yhy01625 | activity:public:2021:cpp:210824 [2021/08/28 22:06:49] (현재) – [C++ 스터디 #15: 템플릿] bkparks12 | ||
---|---|---|---|
줄 1: | 줄 1: | ||
+ | ======C++ 스터디 #15: 템플릿====== | ||
+ | |||
+ | | 시간 | 2021년 8월 24일 화요일 20:00 ~ 22:00 | | ||
+ | | 장소 | ZOOM | | ||
+ | | 참가자 | - | | ||
+ | {{youtube> | ||
+ | =====1. 객체 배열===== | ||
+ | ==== 1.1 객체 배열이란? | ||
+ | class도 배열처럼 한 번에 여러 개를 생성할 수 있을까? \\ | ||
+ | 정수 배열을 생성할 수 있는 것처럼 객체들의 배열도 생성할 수 있다. 이를 **' | ||
+ | 객체 배열을 선언하는 방법을 정수 배열을 생성할 때와 비슷하다. 객체 배열은 다음과 같은 형식으로 선언된다. \\ | ||
+ | '' | ||
+ | \\ | ||
+ | 예를 들면 다음과 같다. \\ | ||
+ | Circle objectArr[3]; | ||
+ | \\ | ||
+ | 객체 배열에 저장된 클래스의 멤버 함수를 호출하는 방법은 다음과 같다. \\ | ||
+ | objectArr[0].calcArea(); | ||
+ | \\ | ||
+ | 객체 배열을 선언하고, | ||
+ | <sxh cpp> | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | class Circle | ||
+ | { | ||
+ | public : | ||
+ | int x, y; | ||
+ | int radius; | ||
+ | Circle() : x(0), y(0), radius(0) { } | ||
+ | Circle(int x, int y, int r) : x(x), y(y), radius(r) { } | ||
+ | void print() | ||
+ | { | ||
+ | cout << " | ||
+ | } | ||
+ | double calcArea() | ||
+ | { | ||
+ | return radius * radius * 3.14; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Circle Array[10]; | ||
+ | |||
+ | for (Circle& | ||
+ | { | ||
+ | c.x = rand() % 500; | ||
+ | c.y = rand() % 300; | ||
+ | c.radius = rand() % 100; | ||
+ | } | ||
+ | |||
+ | for (Circle c : Array) | ||
+ | { | ||
+ | c.print(); | ||
+ | cout << " | ||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== 1.2 array 클래스 ==== | ||
+ | array 클래스를 사용하면 다음과 같은 함수들이 제공되어 기존의 배열에 비해 편리하게 사용할 수 있다. \\ | ||
+ | ^ 함수 ^ 설명 ^ | ||
+ | | size() | 배열의 크기 | | ||
+ | | fill() | 배열의 모든 원소를 동일한 값으로 채운다. | | ||
+ | | empty() | 배열이 비어있는지를 검사한다. | | ||
+ | | at() | 배열의 요소에 접근할 때 사용된다. 물론 [] 기호를 사용하여도 된다. | | ||
+ | | front() | 배열의 첫 번째 요소 | | ||
+ | | back() | 배열의 마지막 요소 | | ||
+ | |||
+ | array 클래스에는 동적으로 크기를 변경하는 기능은 없다. \\ | ||
+ | array 클래스를 사용한 예시를 살펴보자. | ||
+ | |||
+ | <sxh cpp> | ||
+ | # | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | array< | ||
+ | |||
+ | for (int i = 0; i < list.size(); | ||
+ | { | ||
+ | list[i]++; | ||
+ | } | ||
+ | |||
+ | for (auto & elem : list) | ||
+ | { | ||
+ | cout << elem << " "; | ||
+ | } | ||
+ | |||
+ | cout << endl; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | =====2. 예외 처리===== | ||
+ | ==== 2.1 예외 처리 ==== | ||
+ | 잘못된 코드, 부정확한 데이터, 예외적인 상황에 의하여 오류가 발생할 수 있다. 예를 들어 0으로 나누는 것과 같은 잘못된 연산, 배열의 인덱스가 한계를 넘거나 반드시 있어야 할 파일이 없을 수도 있다. \\ | ||
+ | 대개의 경우 오류가 발생하면 프로그램이 종료되는데, | ||
+ | 이를 __' | ||
+ | 다음 코드를 함께 살펴 보자. | ||
+ | |||
+ | <sxh cpp> | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int pizza_slices = 0; | ||
+ | int persons = -1; | ||
+ | int slices_per_person = 0; | ||
+ | |||
+ | cout << " | ||
+ | cin >> pizza_slices; | ||
+ | cout << " | ||
+ | cin >> persons; | ||
+ | | ||
+ | slices_per_person = pizza_slices / persons; | ||
+ | |||
+ | cout << "한 사람당 피자는 " << slices_per_person << " | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | 이 프로그램은 정상적인 값을 입력할 때는 아무 문제가 없다. 그러나 만약 사용자가 사람 수를 0으로 입력한다면 프로그램이 실행 에러를 일으키면서 중단된다. \\ | ||
+ | 따라서 사람 수가 0이 아닌지를 체크하여 오류를 처리 할 수 있다. \\ | ||
+ | |||
+ | ==== 2.2 예외 처리 - if else ==== | ||
+ | 전통적인 오류 처리 방식은 if-else를 사용하여 조건을 검사하는 것이다. \\ | ||
+ | if-else 문을 사용한 다음 코드를 살펴보자. | ||
+ | <sxh cpp> | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int pizza_slices = 0; | ||
+ | int persons = -1; | ||
+ | int slices_per_person = 0; | ||
+ | |||
+ | cout << " | ||
+ | cin >> pizza_slices; | ||
+ | cout << " | ||
+ | cin >> persons; | ||
+ | |||
+ | if (persons == 0) | ||
+ | { | ||
+ | cout << " | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | slices_per_person = pizza_slices / persons; | ||
+ | |||
+ | cout << "한 사람당 피자는 " << slices_per_person << " | ||
+ | } | ||
+ | | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== 2.3 예외 처리 - 예외 처리기==== | ||
+ | if-else의 방식은 검사해야하는 조건이 많아지면 복잡해지고, | ||
+ | 예를 들어 방금의 피자 프로그램에서 피자 조각이 0이나 음수일 경우, 사람수가 음수일 경우 등을 다 처리하려면 프로그램이 굉장히 복잡해진다. \\ | ||
+ | 따라서 조사해야하는 예외가 많아지게 되면 다른 방법을 사용할 수 있다. c++에서는 '' | ||
+ | |||
+ | <sxh cpp> | ||
+ | try | ||
+ | { | ||
+ | //예외가 발생할 수 있는 코드 | ||
+ | if (예외가 발생하면) | ||
+ | throw exception; | ||
+ | } | ||
+ | catch (예외타입 | ||
+ | { | ||
+ | //예외를 처리하는 코드 | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 1. 먼저 try 블록에 예외가 발생할 가능성이 있는 문장이 들어간다. \\ | ||
+ | 2. 만약 예외 조건이 감지되면 throw 문장을 사용하여 예외를 던진다.(예외가 발생되었다는 것을 예외를 던진다고 표현한다.) \\ | ||
+ | 3. 예외가 던져지면 이 예외를 처리하는 catch문으로 점프한다. catch 블록에는 처리할 수 있는 예외의 타입을 지정하고 예외를 처리하기 위한 코드가 들어간다. \\ | ||
+ | \\ | ||
+ | <sxh cpp> | ||
+ | try | ||
+ | { | ||
+ | 문장1; | ||
+ | if (persons == 0) | ||
+ | throw persons; | ||
+ | 문장2; | ||
+ | } | ||
+ | catch (int e) | ||
+ | { | ||
+ | cout << " | ||
+ | } | ||
+ | </ | ||
+ | 다음 코드를 살펴보자.\\ | ||
+ | \\ | ||
+ | 1. 예외가 발생하지 않은 경우\\ | ||
+ | if -> 문장2\\ | ||
+ | \\ | ||
+ | 2. 예외가 발생한 경우\\ | ||
+ | if -> throw -> catch -> cout << " | ||
+ | \\ | ||
+ | 예외가 발생하지 않은 경우와 한 경우 이 코드는 각각 1,2 번과 같은 방식으로 실행된다. \\ | ||
+ | 이 때, throw 문장에서 던진 값이 catch의 매개 변수 e에 전달되므로, | ||
+ | ==== 예제 - 예외 처리기 ==== | ||
+ | if-else 문을 통한 예외 처리에서 배웠던 피자 예제를 try와 catch 블록을 이용하여 처리해 보자. | ||
+ | |||
+ | =====3. 템플릿===== | ||
+ | ==== 3.1 함수 템플릿 ==== | ||
+ | 하나의 형틀을 만들어서 다양한 코드를 생산해 내도록 할 수 있는데 이것을 **' | ||
+ | 함수 템플릿은 함수를 찍어내기 위한 형틀이라고 할 수 있다. 다음 코드를 살펴보자. | ||
+ | |||
+ | <sxh cpp> | ||
+ | int get_max(int x, int y) | ||
+ | { | ||
+ | if (x > y) return x; | ||
+ | else return y; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <sxh cpp> | ||
+ | float get_max(float x, float y) | ||
+ | { | ||
+ | if (x > y) return x; | ||
+ | else return y; | ||
+ | } | ||
+ | </ | ||
+ | 위의 중복 정의된 get_max() 함수에서 달라지는 것은 매개 변수의 타입 뿐이다. double, char 형의 함수가 추가되어도 마찬가지이다. \\ | ||
+ | 이런 경우 이 함수를 일반화 할 수 있다. 이를 **' | ||
+ | <sxh cpp> | ||
+ | ___ get_max(___ x, ___ y) | ||
+ | { | ||
+ | if (x > y) return x; | ||
+ | else return y; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | 이를 함수 템플릿으로 다시 정의하면 다음과 같다. \\ | ||
+ | <sxh cpp> | ||
+ | template< | ||
+ | T get_max(T x, T y) | ||
+ | { | ||
+ | if (x > y) return x; | ||
+ | else return y; | ||
+ | } | ||
+ | </ | ||
+ | 이 코드를 보면 자료형이 변수처럼 표기되어 있다. \\ | ||
+ | 템플릿 함수 정의는 실제 함수를 정의한 것은 아니다. 함수를 생성하는 틀만 정의한 것이다. 실제 함수는 함수를 호출하는 순간, 템플릿에 주어진 매개 변수의 타입에 따라 적절한 함수가 생성된다. \\ | ||
+ | 이제 템플릿을 사용하여 int 형과 double 형의 get_max 함수를 호출해보자. | ||
+ | <sxh cpp> | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | template < | ||
+ | T get_max(T x, T y) | ||
+ | { | ||
+ | if (x > y) return x; | ||
+ | else return y; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | cout << get_max(1, 3) << endl; | ||
+ | cout << get_max(1.2, | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | 템플릿 함수는 두 개 이상의 타입 매개 변수를 가질 수도 있다. 다음 코드는 배열을 다른 배열로 복사하는 템플릿 함수이다. | ||
+ | <sxh cpp> | ||
+ | template < | ||
+ | void copy(T1 a1[], T2 a2[], int n) | ||
+ | { | ||
+ | for (int i = 0; i < n; i++) | ||
+ | { | ||
+ | a1[i] = a2[i]; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | 만약 하나의 타입 매개 변수를 가진다면 이 함수는 배열의 타입이 다르면 오류가 발생하게 된다. | ||
+ | <sxh cpp> | ||
+ | int a[100]; | ||
+ | double b[100]; | ||
+ | copy(a, b, 100); // ERROR | ||
+ | </ | ||
+ | 따라서 이런 경우 위와 같이 타입 매개 변수를 하나 더 사용하는 템플릿 함수를 작성해야 한다. | ||
+ | |||
+ | ==== 3.2 클래스 템플릿 ==== | ||
+ | 클래스 템플릿은 함수 템플릿과 비슷하지만 클래스를 찍어내는 틀이라는 점만 다르다. \\ | ||
+ | 함수 템플릿과 마찬가지로 타입만 다르고 비슷한 동작을 수행하는 클래스를 클래스 템플릿으로 제작할 수 있다. \\ | ||
+ | 클래스 템플릿은 다음과 같은 형식으로 정의한다. | ||
+ | <sxh cpp> | ||
+ | template < | ||
+ | class 클래스이름 | ||
+ | { | ||
+ | // T 어디서든 사용 가능 | ||
+ | |||
+ | }; | ||
+ | </ | ||
+ | |||
+ | 정의된 클래스 템플릿을 사용할 때는 클래스 이름 뒤에 자료형을 붙이면 된다. '' | ||
+ | 선언된 클래스를 circle이라 하고 선언할 클래스 이름을 c1이라 하면 circle< | ||
+ | 클래스 템플릿도 함수 템플릿과 마찬가지로 클래스를 생성하는 틀이기 때문에 구체적 타입이 주어지면 클래스를 생성하게 된다. \\ | ||
+ | 예시 코드를 살펴보자. | ||
+ | <sxh cpp> | ||
+ | # | ||
+ | using namespace std; | ||
+ | |||
+ | template < | ||
+ | class Box | ||
+ | { | ||
+ | T data; | ||
+ | public : | ||
+ | Box() { } | ||
+ | void set(T value) | ||
+ | { | ||
+ | data = value; | ||
+ | } | ||
+ | T get() | ||
+ | { | ||
+ | return data; | ||
+ | } | ||
+ | }; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Box< | ||
+ | box.set(100); | ||
+ | cout << box.get() << endl; | ||
+ | |||
+ | Box< | ||
+ | box1.set(3.14); | ||
+ | cout << box1.get() << endl; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | \\ | ||
+ | 클래스 외부에 멤버 함수를 정의할 경우에는 함수 템플릿처럼 함수 정의 앞에 template< | ||
+ | 생성자나 소멸자도 마찬가지로 template< | ||
+ | 번거로운 형식이지만 이렇게 하지 않으면 컴파일러가 _____를 템플릿 매개 변수로 인식하지 못하기 때문이다. | ||
+ | |||
+ | =====4. 벡터(Vector)===== | ||
+ | ====4.1. 개요==== | ||
+ | 벡터는 자체 메모리 관리를 처리하는 동적 배열 기능을 제공한다.\\ | ||
+ | '' | ||
+ | |||
+ | ====4.2. 선언==== | ||
+ | 벡터는 vector 헤더 파일에 정의되어 있으며, 템플릿 기반이기에 선언 시에 자료형을 명시해야 한다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | vector< | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====4.3. 접근==== | ||
+ | 배열과 마찬가지로 '' | ||
+ | |||
+ | <sxh cpp> | ||
+ | v[3] = 6; | ||
+ | v.at(7) = 4; | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====4.4. 함수==== | ||
+ | ^ 함수 ^ 기능 ^ | ||
+ | | size() | 벡터의 원소 개수 | | ||
+ | | capacity() | 벡터의 크기 | | ||
+ | | begin() | 벡터의 첫번째 위치 | | ||
+ | | end() | 벡터의 마지막 다음 위치 | | ||
+ | | front() | 벡터의 첫번째 요소 | | ||
+ | | back() | 벡터의 마지막 요소 | | ||
+ | | push_back(value) | 벡터의 끝에 value 추가 | | ||
+ | | pop_back() | 벡터의 마지막 요소 삭제 | | ||
+ | \\ | ||
+ | |||
+ | 벡터에 값을 저장하고 출력해보자.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | vector< | ||
+ | int value; | ||
+ | cin >> value; | ||
+ | |||
+ | while (value > 0) | ||
+ | { | ||
+ | v.push_back(value); | ||
+ | cin >> value; | ||
+ | } | ||
+ | for (int i = 0; i < v.size(); i++) | ||
+ | cout << v[i] << " "; // 요소 출력 | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====예제==== | ||
+ | 벡터로 값을 입력받아 평균을 구하는 프로그램을 작성해보자.\\ | ||
+ | Number to be entered : 4 | ||
+ | Enter numbers : 3 52 9 -7 | ||
+ | Average : 14.25 | ||
+ | \\ | ||
+ | |||
+ | =====5. 표준 템플릿 라이브러리(STL)===== | ||
+ | ====5.1. STL이란? | ||
+ | **STL(Standard Template Library)**은 널리 공통적으로 사용되는 자료 구조와 알고리즘을 구현한 클래스들로 이루어져 있다.\\ | ||
+ | STL은 3가지 종류의 컴포넌트(컨테이너, | ||
+ | STL은 프로그래밍에 매우 유용한 컨테이너와 알고리즘을 제공한다. 자신에게 필요한 것을 STL에서 찾아 사용하는 것으로 개발자는 많은 시간과 노력을 절약할 수 있다.\\ | ||
+ | STL은 객체 지향과 일반화 기법을 적용하여 모든 자료형에 대응할 수 있다.\\ | ||
+ | STL은 이미 테스트를 거친 검증된 라이브러리이기 때문에 안심하고 사용할 수 있다. 또한 표준 라이브러리이므로 호환성 문제를 걱정할 필요도 없다.\\ | ||
+ | \\ | ||
+ | |||
+ | ====5.2. 컨테이너==== | ||
+ | 컨테이너는 자료를 저장하는 창고와 같은 역할을 하는 구조이다. 컨테이너는 최대한 일반화 기법을 사용하여 작성되었기 때문에 모든 자료형을 지원한다.\\ | ||
+ | |||
+ | 다음은 몇 가지 일반적인 컨테이너이다.\\ | ||
+ | |||
+ | | 벡터 | 동적 배열처럼 동작, 후입 | | ||
+ | | 큐 | 선입선출 | | ||
+ | | 스택 | 후입선출 | | ||
+ | | 우선 순위 큐 | 우선 순위가 높은 요소가 먼저 출력 | | ||
+ | | 리스트 | 벡터와 유사하나 중간에서 자료 추가가 효율적 | | ||
+ | | 집합 | 중복 없는 자료가 정렬되어 저장 | | ||
+ | | Map | ' | ||
+ | \\ | ||
+ | |||
+ | **컨테이너의 종류**\\ | ||
+ | * 순차 컨테이너 : 자료를 순차적으로 갖고 있는 컨테이너, | ||
+ | * 연관 컨테이너 : 사전 같은 구조로 자료를 저장, 검색을 위한 키를 갖고 있음, 자료 추가는 느리나 탐색은 매우 빠름, ex) Map, 집합 | ||
+ | \\ | ||
+ | |||
+ | ====5.3. 반복자==== | ||
+ | 배열이나 벡터에서는 인덱스를 사용하여 요소에 접근할 수 있었다. 하지만 리스트의 경우 랜덤 접근을 허용치 않아 이때는 인덱스로 접근할 수 없고 포인터를 사용하여야 한다. 그러나 컨테이너 별로 요소에 접근하는 방법이 상이하여 일반적인 방법을 찾아야 했고, 컨테이너의 종류에 관계없이 요소에 접근할 수 있는 일반화된 포인터, 반복자를 사용하게 되었다.\\ | ||
+ | 반복자는 컨테이너에 저장된 요소를 반복적으로 순회하여 각각의 요소를 가리키는 데 사용된다.\\ | ||
+ | 반복자를 사용하면 컨테이너의 유형에 상관없이 동일한 알고리즘을 적용할 수 있다.\\ | ||
+ | |||
+ | **시퀀스**\\ | ||
+ | 시퀀스는 어떤 순서를 갖고 있는 일련의 데이터로, | ||
+ | |||
+ | 반복자에서는 다음 연산자를 사용할 수 있다.\\ | ||
+ | * 다음 요소를 가리키기 위한 '' | ||
+ | * 이전 요소를 가리키기 위한 '' | ||
+ | * 두 반복자가 같은 요소를 가리키는지 확인하는 '' | ||
+ | * 반복자가 가리키는 요소 값을 얻는 역참조 연산자 '' | ||
+ | \\ | ||
+ | 컨테이너는 특별한 위치의 반복자를 얻는 함수를 지원한다.\\ | ||
+ | v.begin()은 컨테이너 v에서 첫 번째 요소를 반환한다.\\ | ||
+ | v.end()는 v에서 마지막 요소를 하나 지난 값을 반환한다. v.end()는 마지막 요소가 아닌 컨테이너의 끝을 나타내는 보초값을 반환한다. \\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | int main() | ||
+ | { | ||
+ | vector< | ||
+ | vector< | ||
+ | v.push_back(1); | ||
+ | v.push_back(2); | ||
+ | v.push_back(3); | ||
+ | for (it = v.begin(); it != v.end(); it++) | ||
+ | cout << *it << " "; // 반복자로 요소 값 추출 | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | **반복자의 종류**\\ | ||
+ | * 전향 반복자 : '' | ||
+ | * 양방향 반복자 : '' | ||
+ | * 무작위 접근 반복자 : '' | ||
+ | |||
+ | 이전 버전의 C++에서는 반복자의 정확한 자료형을 알아야 했다. 하지만 지금은 auto 키워드를 사용하면 반복자의 정확한 자료형을 몰라도 순회가 가능하다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | vector< | ||
+ | v.push_back(1); | ||
+ | v.push_back(2); | ||
+ | v.push_back(3); | ||
+ | for (auto p = v.begin(); p != v.end(); p++) | ||
+ | cout << *p << " "; | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | C++11 부터는 범위 기반 루프 문법을 도입하며 반복자를 사용하지 않고도 반복 접근할 수 있다. | ||
+ | |||
+ | <sxh cpp> | ||
+ | vector< | ||
+ | v.push_back(1); | ||
+ | v.push_back(2); | ||
+ | v.push_back(3); | ||
+ | for (auto n : v) | ||
+ | cout << n << " "; | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | 참조자를 사용하면 요소를 수정할 수도 있다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | int a = 5; | ||
+ | for (auto& n : v) | ||
+ | n = a++; | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====5.4. 컨테이너의 공통 멤버 함수==== | ||
+ | 다음은 모든 컨테이너가 공통으로 지원하는 멤버 함수이다.\\ | ||
+ | ^ 함수 ^ 기능 ^ | ||
+ | | Container() | 기본 생성자 | | ||
+ | | Container(size) | 크기가 size인 컨테이너 생성 | | ||
+ | | Container(size, | ||
+ | | Container(iterator, | ||
+ | | begin() | 첫 번째 요소의 반복자 위치 | | ||
+ | | clear() | 모든 요소 삭제 | | ||
+ | | empty() | 비어있는지 여부 | | ||
+ | | end() | 반복자가 마지막 요소를 지난 위치 | | ||
+ | | erase(iterator) | 컨테이너의 해당 요소 삭제 | | ||
+ | | erase(iterator, | ||
+ | | front() | 컨테이너의 첫 번째 요소 반환 | | ||
+ | | insert(iterator, | ||
+ | | pop_back() | 컨테이너의 마지막 요소 삭제 | | ||
+ | | push_back(value) | 컨테이너의 끝에 value 추가 | | ||
+ | | rbegin() | 끝을 나타내는 역반복자 | | ||
+ | | rend() | 역반복자가 처음을 지난 위치 | | ||
+ | | size() | 컨테이너 크기 | | ||
+ | | opreator=(Container) | 대입 연산자 중복 정의 | | ||
+ | \\ | ||
+ | 모든 컨테이너가 공통으로 지원하기 때문에 컨테이너에서 3의 배수만을 골라 삭제하는 코드는 컨테이너의 종류에 상관없이 항상 다음과 같다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | it = container.begin(); | ||
+ | while (it != container.end()) | ||
+ | { | ||
+ | if ((*it % 3) == 0) | ||
+ | it = container.erase(it); | ||
+ | else | ||
+ | it++; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====5.5. 덱(Deque)==== | ||
+ | 덱은 ' | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | deque< | ||
+ | |||
+ | d.pop_front(); | ||
+ | d.push_front(0); | ||
+ | d.push_back(6); | ||
+ | for (auto n : d) | ||
+ | cout << n << " "; | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ====예제==== | ||
+ | 웹브라우저의 방문 기록 기능을 만들어보자.\\ | ||
+ | 가장 최근에 방문한 KAsimov Wiki를 맨 앞에 추가하고 방문한지 오래된 yahoo!는 끝에서 삭제하자. | ||
+ | |||
+ | NAVER | ||
+ | |||
+ | Daum | ||
+ | yahoo! | ||
+ | ↓ | ||
+ | KAsimov Wiki | ||
+ | NAVER | ||
+ | |||
+ | Daum | ||
+ | \\ | ||
+ | |||
+ | ====5.6. 리스트(List)==== | ||
+ | 리스트는 벡터와 동일하게 순차적인 데이터를 저장하지만 내부 구조가 다르다. 리스트는 이중 연결 리스트로, | ||
+ | |||
+ | 리스트는 공통 멤버 함수에 추가로 push_front(), | ||
+ | |||
+ | 리스트의 중간에 자료를 추가해보자.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | list< | ||
+ | auto it = li.begin(); | ||
+ | it++; | ||
+ | it++; | ||
+ | li.insert(it, | ||
+ | for (auto n : li) | ||
+ | cout << n << " "; | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====5.7. 집합(Set)==== | ||
+ | 벡터나 덱, 리스트에는 자료 간 순서가 있다. 하지만 순서에 상관 없이 자료를 저장할 수도 있다. 이때 사용할 수 있는 구조가 집합(set)과 다중집합(multiset)이다.\\ | ||
+ | 집합에 저장된 자료를 키(key)라고 하며, 집합은 키를 중복해서 가질 수 없지만 다중 집합은 가능하다. | ||
+ | * 집합 : A = { 1, 2, 3, 4, 5 } | ||
+ | * 다중집합 : B = { 1, 1, 2, ,2, 3 } | ||
+ | \\ | ||
+ | 집합에 자료를 추가해 보자.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include <set> | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | set< | ||
+ | s.insert(1); | ||
+ | s.insert(2); | ||
+ | s.insert(3); | ||
+ | |||
+ | auto pos = s.find(2); | ||
+ | if (pos != s.end()) | ||
+ | cout << "값 " << *pos << "가 발견됨" | ||
+ | else | ||
+ | cout << " | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====5.8. Map==== | ||
+ | Map은 많은 데이터에서 원하는 데이터를 빠르게 찾을 수 있는 자료 구조이다. Map에는 key-value 방식으로 자료가 저장되어 있어 key를 제시하여 해당 값을 얻는다. 각 키는 오직 하나의 값에만 매핑될 수 있다. 예시로는 이름-전화번호, | ||
+ | Map 객체를 생성할 때에는 키(key)와 값(value) 두 가지 자료형을 명시해야 한다.\\ | ||
+ | |||
+ | 이름을 키로 맡은 업무를 저장하는 프로그램을 작성해 보자.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #inlcude <map> | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | map< | ||
+ | m.insert(pair< | ||
+ | m[" | ||
+ | |||
+ | for (auto n : m) | ||
+ | cout << n.first << "::" | ||
+ | if (m.find(" | ||
+ | cout << "' | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ====예제==== | ||
+ | 영어 사전을 만들어보자.\\ | ||
+ | 단어를 입력받으면 뜻을 출력한다.\\ | ||
+ | 입력받은 단어가 등록되지 않았으면 뜻을 입력받는다.\\ | ||
+ | |||
+ | 영어 단어를 입력하세요 : apple | ||
+ | 뜻 : 사과 | ||
+ | | ||
+ | 영어 단어를 입력하세요 : house | ||
+ | 등록되지 않은 단어입니다. | ||
+ | house의 뜻을 등록해주세요 : 집 | ||
+ | | ||
+ | 영어 단어를 입력하세요 : house | ||
+ | 뜻 : 집 | ||
+ | \\ | ||
+ | |||
+ | ====5.9. 컨테이너 어댑터==== | ||
+ | 컨테이너 어댑터는 기존의 컨테이너를 변경하여 새로운 기능을 제공하는 클래스이다. 기존 컨테이너의 기능을 그대로 유지하면서 새로운 기능이나 인터페이스를 제공할 수 있다.\\ | ||
+ | |||
+ | ===5.9.1. 스택(Stack)=== | ||
+ | 스택은 후입 선출(LIFO : Last In First Out) 구조로, 나중에 들어온 데이터가 먼저 나간다. 스택은 컴퓨터에 없어서는 안 될 중요한 자료 구조이다. 함수 호출이 바로 스택을 사용하여 이루어진다. 함수가 호출되고 복귀할 때 스택에 저장된 순서대로 수행된다.\\ | ||
+ | 스택을 생성할 때는 지금까지 컨테이너를 생성한 것과 똑같이 수행하면 된다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | stack< | ||
+ | stack< | ||
+ | </ | ||
+ | |||
+ | 스택을 구현하는데 사용되는 기반 컨테이너를 변경하려면 다음과 같이 두 번째 인자를 주면 된다.\\ | ||
+ | |||
+ | <sxh cpp> | ||
+ | stack< | ||
+ | </ | ||
+ | |||
+ | 스택에서는 다음 함수들이 제공된다.\\ | ||
+ | |||
+ | ^ 함수 ^ 기능 ^ | ||
+ | | push(value) | 스택에 value 삽입 | | ||
+ | | pop() | 스택의 상단 원소 삭제 | | ||
+ | | top() | 스택의 상단 원소 반환 | | ||
+ | | empty() | 스택의 공백 여부 | | ||
+ | | size() | 스택의 원소 개수 | | ||
+ | \\ | ||
+ | <sxh cpp> | ||
+ | #include < | ||
+ | #include < | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | stack< | ||
+ | st.push(1); | ||
+ | st.push(2); | ||
+ | st.push(3); | ||
+ | cout << "size : " << st.size() << endl; | ||
+ | cout << "empty : " << (st.empty() ? " | ||
+ | while(!st.empty()) | ||
+ | { | ||
+ | cout << "top : " << st.top() << endl; | ||
+ | st.pop(); | ||
+ | } | ||
+ | cout << "size : " << st.size() << endl; | ||
+ | cout << "empty : " << (st.empty() ? " | ||
+ | return 0; | ||
+ | } | ||
+ | </ | ||
+ | \\ | ||
+ | |||
+ | ===5.9.2. 큐(Queue)=== | ||
+ | 큐는 먼저 들어온 데이터가 먼저 나가는 선입선출(FIFO : First In First Out) 자료구조이다.\\ | ||
+ | 큐는 데이터를 처리하기 전 잠시 저장하는 용도로 사용되며, | ||
+ | |||
+ | 큐에서는 다음 함수들이 제공된다.\\ | ||
+ | ^ 함수 ^ 기능 ^ | ||
+ | | push(value) | 큐의 끝에 value 삽입 | | ||
+ | | pop() | 큐의 첫 번째 원소 삭제 | | ||
+ | | front() | 큐의 첫 번째 원소 반환 | | ||
+ | | back() | 큐의 마지막 원소 반환 | | ||
+ | | empty() | 큐의 공백 여부 | | ||
+ | | size() | 큐의 원소 개수 | | ||