C++ 스터디 #15: 템플릿

시간 2021년 8월 24일 화요일 20:00 ~ 22:00
장소 ZOOM
참가자 -

class도 배열처럼 한 번에 여러 개를 생성할 수 있을까?
정수 배열을 생성할 수 있는 것처럼 객체들의 배열도 생성할 수 있다. 이를 '객체 배열' 이라고 한다.
객체 배열을 선언하는 방법을 정수 배열을 생성할 때와 비슷하다. 객체 배열은 다음과 같은 형식으로 선언된다.
클래스_이름 배열_이름[배열_크기];

예를 들면 다음과 같다.

Circle objectArr[3];


객체 배열에 저장된 클래스의 멤버 함수를 호출하는 방법은 다음과 같다.

objectArr[0].calcArea();


객체 배열을 선언하고, 멤버 함수를 호출하는 방법을 짧은 코드와 함께 알아보자.

#include<iostream>
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 << "반지름 : " << radius << "@(" << x << ", " << y << "),  ";
    }
    double calcArea()
    {
        return radius * radius * 3.14;
    }
};

int main()
{
    Circle Array[10];

    for (Circle& c : Array)
    {
        c.x = rand() % 500;
        c.y = rand() % 300;
        c.radius = rand() % 100;
    }

    for (Circle c : Array)
    {
        c.print();
        cout << "넓이 : " << c.calcArea() << endl;
    }
       
    return 0;
}

array 클래스를 사용하면 다음과 같은 함수들이 제공되어 기존의 배열에 비해 편리하게 사용할 수 있다.

함수 설명
size() 배열의 크기
fill() 배열의 모든 원소를 동일한 값으로 채운다.
empty() 배열이 비어있는지를 검사한다.
at() 배열의 요소에 접근할 때 사용된다. 물론 [] 기호를 사용하여도 된다.
front() 배열의 첫 번째 요소
back() 배열의 마지막 요소

array 클래스에는 동적으로 크기를 변경하는 기능은 없다.
array 클래스를 사용한 예시를 살펴보자.

#include<iostream>
#include<array>
using namespace std;

int main()
{
    array<int, 3> list{ 1,2,3 };

    for (int i = 0; i < list.size(); i++)
    {
        list[i]++;
    }

    for (auto & elem : list)
    {
        cout << elem << " ";
    }

    cout << endl;

    return 0;
}

잘못된 코드, 부정확한 데이터, 예외적인 상황에 의하여 오류가 발생할 수 있다. 예를 들어 0으로 나누는 것과 같은 잘못된 연산, 배열의 인덱스가 한계를 넘거나 반드시 있어야 할 파일이 없을 수도 있다.
대개의 경우 오류가 발생하면 프로그램이 종료되는데, 프로그램에서 오류를 감지하여 오류를 처리한 후 계속 실행할 수 있게 한다면 더 나은 프로그램이 될 수 있다.
이를 '예외 처리'라 한다.
다음 코드를 함께 살펴 보자.

 
#include<iostream>
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 << "입니다. " << endl;

    return 0;
}
이 프로그램은 정상적인 값을 입력할 때는 아무 문제가 없다. 그러나 만약 사용자가 사람 수를 0으로 입력한다면 프로그램이 실행 에러를 일으키면서 중단된다.
따라서 사람 수가 0이 아닌지를 체크하여 오류를 처리 할 수 있다.

전통적인 오류 처리 방식은 if-else를 사용하여 조건을 검사하는 것이다.
if-else 문을 사용한 다음 코드를 살펴보자.

#include<iostream>
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 << "사람이 0명 입니다. " << endl;
    }
    else
    {
        slices_per_person = pizza_slices / persons;

        cout << "한 사람당 피자는 " << slices_per_person << "입니다. " << endl;
    }
    
    return 0;
}

if-else의 방식은 검사해야하는 조건이 많아지면 복잡해지고, 어떤 코드가 정상적인 실행이고 어떤 코드가 오류 처리 코드인지를 구별하기 힘들어진다.
예를 들어 방금의 피자 프로그램에서 피자 조각이 0이나 음수일 경우, 사람수가 음수일 경우 등을 다 처리하려면 프로그램이 굉장히 복잡해진다.
따라서 조사해야하는 예외가 많아지게 되면 다른 방법을 사용할 수 있다. c++에서는 try, throw, catch 의 키워드를 사용한 예외 처리 형식을 사용한다. 예외 처리기의 기본 형식은 다음과 같다.

try
{
    //예외가 발생할 수 있는 코드
    if (예외가 발생하면)
        throw exception;
}
catch (예외타입   매개변수)
{
    //예외를 처리하는 코드
}

1. 먼저 try 블록에 예외가 발생할 가능성이 있는 문장이 들어간다.
2. 만약 예외 조건이 감지되면 throw 문장을 사용하여 예외를 던진다.(예외가 발생되었다는 것을 예외를 던진다고 표현한다.)
3. 예외가 던져지면 이 예외를 처리하는 catch문으로 점프한다. catch 블록에는 처리할 수 있는 예외의 타입을 지정하고 예외를 처리하기 위한 코드가 들어간다.

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에 전달되므로, throw에서 던진 값인 persons와 catch의 매개 변수 e의 타입이 일치해야 한다.

if-else 문을 통한 예외 처리에서 배웠던 피자 예제를 try와 catch 블록을 이용하여 처리해 보자.

하나의 형틀을 만들어서 다양한 코드를 생산해 내도록 할 수 있는데 이것을 '템플릿' 이라고 한다.
함수 템플릿은 함수를 찍어내기 위한 형틀이라고 할 수 있다. 다음 코드를 살펴보자.

int get_max(int x, int y)
{
    if (x > y) return x;
    else return y;
}

float get_max(float x, float y)
{
    if (x > y) return x;
    else return y;
}
위의 중복 정의된 get_max() 함수에서 달라지는 것은 매개 변수의 타입 뿐이다. double, char 형의 함수가 추가되어도 마찬가지이다.
이런 경우 이 함수를 일반화 할 수 있다. 이를 '일반화 프로그래밍' 이라고 한다.
___ get_max(___ x, ___ y)
{
    if (x > y) return x;
    else return y;
}

이를 함수 템플릿으로 다시 정의하면 다음과 같다.
template<typename T>
T get_max(T x, T y)
{
    if (x > y) return x;
    else return y;
}
이 코드를 보면 자료형이 변수처럼 표기되어 있다.
템플릿 함수 정의는 실제 함수를 정의한 것은 아니다. 함수를 생성하는 틀만 정의한 것이다. 실제 함수는 함수를 호출하는 순간, 템플릿에 주어진 매개 변수의 타입에 따라 적절한 함수가 생성된다.
이제 템플릿을 사용하여 int 형과 double 형의 get_max 함수를 호출해보자.
#include<iostream>
using namespace std;

template <typename T>
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, 3.9) << endl;

    return 0;
}

템플릿 함수는 두 개 이상의 타입 매개 변수를 가질 수도 있다. 다음 코드는 배열을 다른 배열로 복사하는 템플릿 함수이다.
template <typename T1, typename T2>
void copy(T1 a1[], T2 a2[], int n)
{
    for (int i = 0; i < n; i++)
    {
        a1[i] = a2[i];
    }
}
만약 하나의 타입 매개 변수를 가진다면 이 함수는 배열의 타입이 다르면 오류가 발생하게 된다.
int a[100];
double b[100];
copy(a, b, 100); // ERROR
따라서 이런 경우 위와 같이 타입 매개 변수를 하나 더 사용하는 템플릿 함수를 작성해야 한다.

클래스 템플릿은 함수 템플릿과 비슷하지만 클래스를 찍어내는 틀이라는 점만 다르다.
함수 템플릿과 마찬가지로 타입만 다르고 비슷한 동작을 수행하는 클래스를 클래스 템플릿으로 제작할 수 있다.
클래스 템플릿은 다음과 같은 형식으로 정의한다.

template <typename T>
class 클래스이름
{
    // T 어디서든 사용 가능

};

정의된 클래스 템플릿을 사용할 때는 클래스 이름 뒤에 자료형을 붙이면 된다. 클래스이름<자료형> 클래스1;
선언된 클래스를 circle이라 하고 선언할 클래스 이름을 c1이라 하면 circle<int> c1; 이렇게 사용하면 된다.
클래스 템플릿도 함수 템플릿과 마찬가지로 클래스를 생성하는 틀이기 때문에 구체적 타입이 주어지면 클래스를 생성하게 된다.
예시 코드를 살펴보자.

#include<iostream>
using namespace std;

template <typename T>
class Box
{
    T data;
public : 
    Box() { }
    void set(T value)
    {
        data = value;
    }
    T get()
    {
        return data;
    }
};

int main()
{
    Box<int> box;
    box.set(100);
    cout << box.get() << endl;

    Box<double> box1;
    box1.set(3.14);
    cout << box1.get() << endl;

    return 0;
}


클래스 외부에 멤버 함수를 정의할 경우에는 함수 템플릿처럼 함수 정의 앞에 template<typename _> 를 써주어야 한다. 멤버 함수를 정의할 때마다 되풀이하여 써주어야 한다는 점을 주의해야 한다.
생성자나 소멸자도 마찬가지로 template<typename _> 문장이 함수 앞에 위치하여야 한다.
번거로운 형식이지만 이렇게 하지 않으면 컴파일러가 _를 템플릿 매개 변수로 인식하지 못하기 때문이다.

벡터는 자체 메모리 관리를 처리하는 동적 배열 기능을 제공한다.
newdelete를 사용하여 메모리를 동적으로 할당, 해제하는 과정을 코드로 만들어 주지 않고도 크기를 동적으로 갖는 배열을 만들 수 있다.

벡터는 vector 헤더 파일에 정의되어 있으며, 템플릿 기반이기에 선언 시에 자료형을 명시해야 한다.

#include <vector>							// vector가 들어있는 헤더파일
vector<int> v;							// int형 벡터 생성
vector<int> v = { 1, 2, 3 };				// int형 백터 생성 후 1, 2, 3 으로 초기화
vector<int> v[10];						// int형 벡터 배열(크기 10) 생성
vector<int> v[] = { { 1, 2 }, { 3, 4 } };	// int형 백터 배열 생성(행은 가변이지만 열은 고정)
vector<vector<int>> v;					  // 2차원 백터 생성(행과 열 모두 가변)
vector<int> v(5);							// 5개의 원소를 0으로 초기화
vector<int> v(5, 3);						// 5개의 원소를 3으로 초기화
vector<int> v2(v);						// 벡터 v를 복사하여 벡터v2 생성

배열과 마찬가지로 [ ] 연산자 혹은 at() 함수를 통해 수행할 수 있다.

v[3] = 6;
v.at(7) = 4;

함수 기능
size() 벡터의 원소 개수
capacity() 벡터의 크기
begin() 벡터의 첫번째 위치
end() 벡터의 마지막 다음 위치
front() 벡터의 첫번째 요소
back() 벡터의 마지막 요소
push_back(value) 벡터의 끝에 value 추가
pop_back() 벡터의 마지막 요소 삭제


벡터에 값을 저장하고 출력해보자.

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;		// 벡터 선언
    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


STL(Standard Template Library)은 널리 공통적으로 사용되는 자료 구조와 알고리즘을 구현한 클래스들로 이루어져 있다.
STL은 3가지 종류의 컴포넌트(컨테이너, 반복자, 알고리즘)를 제공한다.
STL은 프로그래밍에 매우 유용한 컨테이너와 알고리즘을 제공한다. 자신에게 필요한 것을 STL에서 찾아 사용하는 것으로 개발자는 많은 시간과 노력을 절약할 수 있다.
STL은 객체 지향과 일반화 기법을 적용하여 모든 자료형에 대응할 수 있다.
STL은 이미 테스트를 거친 검증된 라이브러리이기 때문에 안심하고 사용할 수 있다. 또한 표준 라이브러리이므로 호환성 문제를 걱정할 필요도 없다.

컨테이너는 자료를 저장하는 창고와 같은 역할을 하는 구조이다. 컨테이너는 최대한 일반화 기법을 사용하여 작성되었기 때문에 모든 자료형을 지원한다.

다음은 몇 가지 일반적인 컨테이너이다.

벡터 동적 배열처럼 동작, 후입
선입선출
스택 후입선출
우선 순위 큐 우선 순위가 높은 요소가 먼저 출력
리스트 벡터와 유사하나 중간에서 자료 추가가 효율적
집합 중복 없는 자료가 정렬되어 저장
Map '키-값'의 형식으로 저장, 키를 제시하면 해당 값을 찾을 수 있음


컨테이너의 종류

  • 순차 컨테이너 : 자료를 순차적으로 갖고 있는 컨테이너, 자료의 추가는 빠르나 탐색은 느림, ex) 벡터, 리스트
  • 연관 컨테이너 : 사전 같은 구조로 자료를 저장, 검색을 위한 키를 갖고 있음, 자료 추가는 느리나 탐색은 매우 빠름, ex) Map, 집합


배열이나 벡터에서는 인덱스를 사용하여 요소에 접근할 수 있었다. 하지만 리스트의 경우 랜덤 접근을 허용치 않아 이때는 인덱스로 접근할 수 없고 포인터를 사용하여야 한다. 그러나 컨테이너 별로 요소에 접근하는 방법이 상이하여 일반적인 방법을 찾아야 했고, 컨테이너의 종류에 관계없이 요소에 접근할 수 있는 일반화된 포인터, 반복자를 사용하게 되었다.
반복자는 컨테이너에 저장된 요소를 반복적으로 순회하여 각각의 요소를 가리키는 데 사용된다.
반복자를 사용하면 컨테이너의 유형에 상관없이 동일한 알고리즘을 적용할 수 있다.

시퀀스
시퀀스는 어떤 순서를 갖고 있는 일련의 데이터로, 시퀀스에는 시작과 끝이 있다. 반복자는 시퀀스의 요소를 순회하며 식별하는 객체이다.

반복자에서는 다음 연산자를 사용할 수 있다.

  • 다음 요소를 가리키기 위한 ++ 연산자
  • 이전 요소를 가리키기 위한 연산자
  • 두 반복자가 같은 요소를 가리키는지 확인하는 ==!= 연산자
  • 반복자가 가리키는 요소 값을 얻는 역참조 연산자 *


컨테이너는 특별한 위치의 반복자를 얻는 함수를 지원한다.
v.begin()은 컨테이너 v에서 첫 번째 요소를 반환한다.
v.end()는 v에서 마지막 요소를 하나 지난 값을 반환한다. v.end()는 마지막 요소가 아닌 컨테이너의 끝을 나타내는 보초값을 반환한다.

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> v;		// 벡터 선언
    vector<int>::iterator it;	// 반복자 선언
    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 키워드를 사용하면 반복자의 정확한 자료형을 몰라도 순회가 가능하다.

vector<int> v;
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 부터는 범위 기반 루프 문법을 도입하며 반복자를 사용하지 않고도 반복 접근할 수 있다.

vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for (auto n : v)
    cout << n << " ";

참조자를 사용하면 요소를 수정할 수도 있다.

int a = 5;
for (auto& n : v)
    n = a++;

다음은 모든 컨테이너가 공통으로 지원하는 멤버 함수이다.

함수 기능
Container() 기본 생성자
Container(size) 크기가 size인 컨테이너 생성
Container(size, value) 크기가 size, 초깃값이 value인 컨테이너 생성
Container(iterator, iterator) 다른 컨테이너로부터 초깃값 범위를 받아 생성
begin() 첫 번째 요소의 반복자 위치
clear() 모든 요소 삭제
empty() 비어있는지 여부
end() 반복자가 마지막 요소를 지난 위치
erase(iterator) 컨테이너의 해당 요소 삭제
erase(iterator, iterator) 컨테이너의 지정 범위 삭제
front() 컨테이너의 첫 번째 요소 반환
insert(iterator, value) 컨테이너의 해당 위치에 value 삽입
pop_back() 컨테이너의 마지막 요소 삭제
push_back(value) 컨테이너의 끝에 value 추가
rbegin() 끝을 나타내는 역반복자
rend() 역반복자가 처음을 지난 위치
size() 컨테이너 크기
opreator=(Container) 대입 연산자 중복 정의


모든 컨테이너가 공통으로 지원하기 때문에 컨테이너에서 3의 배수만을 골라 삭제하는 코드는 컨테이너의 종류에 상관없이 항상 다음과 같다.

it = container.begin();
while (it != container.end())
{
    if ((*it % 3) == 0)
        it = container.erase(it);  // 현재 요소 삭제 후 다음 요소 위치 반환
    else
        it++;
}

덱은 'double-ended queue로, 양방향으로 커지는 동적 배열이다. 덱은 벡터와 달리 앞과 뒤에서 모두 요소를 추가하거나 삭제할 수 있다.

#include <iostream>
#include <deque>
using namespace std;

int main()
{
    deque<int> d = { 1, 2, 3, 4, 5 };	// 덱 선언

    d.pop_front();			// 앞에서 삭제
    d.push_front(0);			// 앞에서 추가
    d.push_back(6);			// 뒤에서 추가
    for (auto n : d)
        cout << n << " ";
    return 0;
}

웹브라우저의 방문 기록 기능을 만들어보자.
가장 최근에 방문한 KAsimov Wiki를 맨 앞에 추가하고 방문한지 오래된 yahoo!는 끝에서 삭제하자.

NAVER
Google
Daum
yahoo!
   ↓
KAsimov Wiki
NAVER
Google
Daum


리스트는 벡터와 동일하게 순차적인 데이터를 저장하지만 내부 구조가 다르다. 리스트는 이중 연결 리스트로, 중간 위치에서 삽입이나 삭제가 빈번한 경우에 효율적인 자료 구조이다. 이중 연결 리스트에서는 모든 노드가 앞과 뒤를 가리키는 포인터를 모두 갖고 있어 양방향으로 이동이 가능하다. 또, 벡터와는 다르게 [] 연산자를 지원하지 않아 어떤 요소에 접근하려면 첫 번째 요소부터 순차 접근해야 한다.

리스트는 공통 멤버 함수에 추가로 push_front(), pop_front(), remove(), unique(), merge(), reverse(), sort(), splice() 등을 갖고 있다.

리스트의 중간에 자료를 추가해보자.

#include <iostream>
#include <list>
using namespace std;

int main()
{
    list<int> li = { 10, 20, 30, 40 };
    auto it = li.begin();
    it++;
    it++;
    li.insert(it, 25);
    for (auto n : li)
        cout << n << " ";
    return 0;
}

벡터나 덱, 리스트에는 자료 간 순서가 있다. 하지만 순서에 상관 없이 자료를 저장할 수도 있다. 이때 사용할 수 있는 구조가 집합(set)과 다중집합(multiset)이다.
집합에 저장된 자료를 키(key)라고 하며, 집합은 키를 중복해서 가질 수 없지만 다중 집합은 가능하다.

  • 집합 : A = { 1, 2, 3, 4, 5 }
  • 다중집합 : B = { 1, 1, 2, ,2, 3 }


집합에 자료를 추가해 보자.

#include <iostream>
#include <set>
using namespace std;

int main()
{
    set<int> s;
    s.insert(1);
    s.insert(2);
    s.insert(3);

    auto pos = s.find(2);
    if (pos != s.end())
        cout << "값 " << *pos << "가 발견됨" << endl;
    else
        cout << "값이 발견되지 않음"

    return 0;
}

Map은 많은 데이터에서 원하는 데이터를 빠르게 찾을 수 있는 자료 구조이다. Map에는 key-value 방식으로 자료가 저장되어 있어 key를 제시하여 해당 값을 얻는다. 각 키는 오직 하나의 값에만 매핑될 수 있다. 예시로는 이름-전화번호, 학번-이름 등이 있다.
Map 객체를 생성할 때에는 키(key)와 값(value) 두 가지 자료형을 명시해야 한다.

이름을 키로 맡은 업무를 저장하는 프로그램을 작성해 보자.

#include <iostream>
#inlcude <map>
using namespace std;

int main()
{
    map<string, string> m;
    m.insert(pair<string, string>("김희연", "총무");
    m["윤호연"] = "SW";

    for (auto n : m)
        cout << n.first << "::" << n.second << endl;
    if (m.find("홍길동") == m.end())
        cout << "'홍길동'은 발견되지 않았습니다.";

    return 0;
}

영어 사전을 만들어보자.
단어를 입력받으면 뜻을 출력한다.
입력받은 단어가 등록되지 않았으면 뜻을 입력받는다.

영어 단어를 입력하세요 : apple
뜻 : 사과

영어 단어를 입력하세요 : house
등록되지 않은 단어입니다.
house의 뜻을 등록해주세요 : 집

영어 단어를 입력하세요 : house
뜻 : 집


컨테이너 어댑터는 기존의 컨테이너를 변경하여 새로운 기능을 제공하는 클래스이다. 기존 컨테이너의 기능을 그대로 유지하면서 새로운 기능이나 인터페이스를 제공할 수 있다.

5.9.1. 스택(Stack)

스택은 후입 선출(LIFO : Last In First Out) 구조로, 나중에 들어온 데이터가 먼저 나간다. 스택은 컴퓨터에 없어서는 안 될 중요한 자료 구조이다. 함수 호출이 바로 스택을 사용하여 이루어진다. 함수가 호출되고 복귀할 때 스택에 저장된 순서대로 수행된다.
스택을 생성할 때는 지금까지 컨테이너를 생성한 것과 똑같이 수행하면 된다.

stack<int> st;
stack<string> st2;

스택을 구현하는데 사용되는 기반 컨테이너를 변경하려면 다음과 같이 두 번째 인자를 주면 된다.

stack<string, vector<string>> st;  // 벡터를 사용하여 스택을 구현

스택에서는 다음 함수들이 제공된다.

함수 기능
push(value) 스택에 value 삽입
pop() 스택의 상단 원소 삭제
top() 스택의 상단 원소 반환
empty() 스택의 공백 여부
size() 스택의 원소 개수


#include <iostream>
#include <stack>
using namespace std;

int main()
{
    stack<int> st;
    st.push(1);
    st.push(2);
    st.push(3);
    cout << "size : " << st.size() << endl;
    cout << "empty : " << (st.empty() ? "YES" : "NO") << endl;
    while(!st.empty())
    {
        cout << "top : " << st.top() << endl;
        st.pop();
    }
    cout << "size : " << st.size() << endl;
    cout << "empty : " << (st.empty() ? "YES" : "NO") << endl;
    return 0;
}

5.9.2. 큐(Queue)

큐는 먼저 들어온 데이터가 먼저 나가는 선입선출(FIFO : First In First Out) 자료구조이다.
큐는 데이터를 처리하기 전 잠시 저장하는 용도로 사용되며, 모든 전자제품에 존재하는 버퍼가 바로 큐를 통해 구현된다. 큐는 후단(tail)에서 데이터를 추가하고 전단(head)에서 데이터를 삭제한다.

큐에서는 다음 함수들이 제공된다.

함수 기능
push(value) 큐의 끝에 value 삽입
pop() 큐의 첫 번째 원소 삭제
front() 큐의 첫 번째 원소 반환
back() 큐의 마지막 원소 반환
empty() 큐의 공백 여부
size() 큐의 원소 개수
  • activity/public/2021/cpp/210824.txt
  • 마지막으로 수정됨: 3년 전
  • 저자 bkparks12