C++의 표준 템플릿 라이브러리인 STL. CPP의 특징인 일반화 프로그래밍 패러다임의 한 축을 담당한다. STL은 객체지향 방식에서 조금 더 나아가, 알고리즘을 일반화한 표현 방식을 제공하여, 데이터를 추상화하고 코드를 재활용할 수 있게 한다.
같은 타입의 여러 객체를 저장하는 일종의 집합 개념.
컨테이너의 각 요소에 '반복자'(iterator)을 통해 접근할 수 있음.
컨테이너 내 요소 추가 및 제거를 포함한 다양한 작업을 도와주는 여러 멤버 함수를 포함하고 있음.
STL에서 컨테이너는 자료를 저장하는 방식과 관리하는 방식에 따라 여러 가지 형태로 나뉠 수 있습니다.
STL 컨테이너는 크게 다음과 같이 세 가지 유형으로 구분됩니다.
1. 시퀀스 컨테이너(sequence container)
- 데이터를 선형으로 저장하며, 특별한 제약이나 규칙이 없는 가장 일반적인 컨테이너
삽입된 요소의 순서가 그대로 유지된다
명확한 순서가 유지되는 요소들이므로, 특정 위치를 참조하는 연산이 가능해야 한다
vector, deque, list, forward_list
2. 연관 컨테이너(associative container)
- key와 value처럼 관련 있는 데이터를 하나의 쌍으로 저장하는 컨테이너
set, multiset, map, multimap
3. 컨테이너 어댑터(adapter container)
- 간결함과 명료성을 위해 인터페이스를 제한한 시퀀스 컨테이너나 연관 컨테이너의 변형.
- 반복자를 지원하지 않으므로 STL 알고리즘에서는 사용할 수 없다.
stack, queue, priority_queue
지금까지 우리는 배열의 요소에 접근할 때 i 같은 인덱스를 선언해 사용해 왔다. 반복자는 그런 연산의 과정을 어떤 객체에서나 동일하게 사용할 수 있도록 통일해서 cpp에서 제공하는 generic 포인터 객체라고 생각하면 된다. iterator 클래스 내부에는 배열의 요소를 쉽게 가리킬 수 있도록 몇가지 멤버함수들이 정의되어 있다. iterator 메서드와 컨테이너 메서드를 적절히 사용하면 좋은 알고리즘을 가진 클래스와 함수를 작성할 수 있을 것이다.
#include <vector> //헤더파일 추가
vector<[data type]> [변수이름] //선언
vector<int> vc = {10, 20, 30}; // vector 객체의 선언 및 초기화
cout << "현재 벡터의 크기는 " << vc.size() << "입니다." << endl;
// 현재 벡터의 크기는 3입니다.
vc.push_back(40); // vector 요소의 추가
cout << "현재 벡터의 크기는 " << vc.size() << "입니다." << endl;
// 현재 벡터의 크기는 4입니다.
cout << "현재 벡터의 네 번째 요소는 " << vc[3] << "입니다." << endl
// 현재 벡터의 네 번째 요소는 40입니다.
cout << "현재 벡터의 모든 요소는 다음과 같습니다 :" << endl;
copy(vc.begin(), vc.end(), ostream_iterator<int>(cout, " "));
// 현재 벡터의 모든 요소는 다음과 같습니다 :
// 10 20 30 40
시퀀스 컨테이너
이중 연결 리스트(doubly linked list)의 클래스 템플릿 표현
컨테이너의 모든 요소에서 양방향 접근, 빠른 삽입과 삭제가 가능
임의 접근 불가
따로따로. 값을 넣을 때마다 메모리를 할당. (배열처럼 사용 불가, Linked List처럼 동작).
임의 접근을 통한 빠른 접근이 장점
요소들의 빠른 삽입과 삭제가 장점입니다.
리스트를 구성하는 링크(link)는 포인터이므로, 다음과 같은 특정 작업을 링크만 재배치하는 것으로 아주 빠르게 수행할 수 있음
swap() 두 요소의 위치를 서로 바꿈.
reverse() 리스트 전체의 요소의 위치를 역순으로 변경함.
sort() 리스트 전체의 요소를 정렬함.
unique() 같은 값이 인접해 있을 경우, 그 값들을 하나로 단일화함.
merge() 두 정렬된 리스트를 합병함.
splice() 두 리스트를 연결하거나, 한 쪽 리스트로 이동시킴.
Vector는 값을 삭제하더라도 메모리 해제를 하지 않는다. 심지어 Clear를 하더라도 메모리는 그대로 남게 된다.
반대로 List의 경우 변수가 해제 될때마다 메모리에서 해제하게 된다. 할당과 마찬가지로 해제또한 popback의 경우 vector가 훨씬 빠르다. 하지만 역시 중간에 값을 삭제하는 것은 List가 더 빠르다.
따라서 순서와 상관없거나 순차적으로만 추가, 삭제되는 데이터는 vector를 쓰는게 좋다. 임의 접근을 통한 빠른 접근이 장점이다.
순서가 중요하여 리스트의 중간위치에 값이 추가, 삭제가 되는 경우 list를 사용하는 것이 좋다. 요소들의 빠른 삽입과 삭제가 장점이다. (메모리는 더 먹지만 속도가 빠르다)
벡터에서, 반복자가 가리키는 요소를 삭제 할 경우 그 반복자는 무효화 된다.
vector에 대해 erase를 호출하면 방금 삭제된 요소 다음에 있는 요소들을 가리키는 모든 반복자는 무효화 된다.
또한 push_back을 사용하여 vector에 요소를 추가해도 해당 vector를 가리키고 있던 모든 반복자는 무효화 된다.
vector에 한 요소를 삭제하면 그 다음 요소들이 이동되고, 한 요소를 추가하면 새로운 요소를 위한 공간을 확보하기 위해 전체 vector가 재할당되기 때문이다.
하지만 list에서는 erase나 push_back이 다른 요소들에 대한 반복자를 무효화 시키지 않고 실제로 삭제된 요소를 가리키는 반복자만 무효화 된다. 왜냐하면 그 요소는 더 이상 존재하지 않기 때문이다.
-std::stack 컨테이너는 훌륭하지만, iterable이 아닌 유일한 STL 컨테이너 입니다. iterator를 std::stack 컨테이너에 접목시켜 이 문제를 해결해보세요.
typedef typename std::stack<T>::container_type::iterator iterator;
std::stack을 상속받은, 타입 T형의 인스턴스를 만들고, 이 인스턴스 내의 iterator을 가져와서 iterator이라는 멤버로 만들어 주겠다는 뜻임
stack 라이브러리를 까보면
template<class _Ty,
class _Container = deque<_Ty> >
class stack
{
// ...
protected:
_Container c; // the underlying container
};
요런 부분이 있는데, protected접근권한자로 _Container 타입의 멤버변수가 있고, 이 타입이 deque임. 요 c 멤버변수로 접근하면 deque의 멤버함수들을 호출할 수 있고, stack을 상속받아 c 변수로 접근 가능하다면 deque의 함수들을 사용할 수 있음.
stack에서 사용하는 empty, size, top, push, pop등의 함수들도 내부에서는 deque함수를 호출해서 구현되어 있음.
deque는 vector의 단점을 보완하기 위해 만들어진 container임. 메모리가 부족하면 메모리를 처음부터 재할당하여 원소를 복사하는 vector의 단점 보완하기 위해, deque는 메모리가 부족할 때 마다 일정한 크기의 새로운 메모리 블럭을 할당함.
dq.begin();
dq.end();
dq.rbegin();
dq.rend();
출처: https://blockdmask.tistory.com/73 [개발자 지망생]