C++ STL - Container[5]
이번 시간에는 STL의 Container에 대해서 알아보겠습니다.
STL Container 특징
1. Container basic
-
시퀀스 컨테이너 : 요소가 삽입된 순서대로 놓여있는 컨테이너
- list, vector, deque, forward_list, array
-
연관 컨테이너
- 삽입 순서와 상관없이 규칙을 가지고 요소를 보관
- set, muti-set, map, multi-map, unordered_set, unordered-multiset, unordered_map, unordered_multimap
-
Adapter container
- stack, queue, priority_queue
2. 공통 특징
- 값을 보관함
- 멤버 함수 뿐 아니라 member type 제공
- 반복자 제공
- 제거와 리턴을 동시에 하지 않음 ( .back()과 .pop_back() )
3. Policy base design
- 메모리 할당 방식을 제공(Allocator)
#include <iostream>
using namespace std;
template<typename T,
typename Allocator = allocator<T> > class vector
{
//....
Allocator ax;
public:
void push_back(const T& a)
{
// 메모리 할당이 필요하다.
// new, malloc, 공유 메모리, pool,
T* p = ax.allocate(1);
ax.deallocate(p, 1);
}
};
int main()
{
vector<int, MyAlloc> v;
v.push_back(10);
}
- 단위 전략 디자인 제공
#include <iostream>
#include <string>
using namespace std;
#include <cctype>
struct ci_char_traits : public std::char_traits<char>
{
static char to_upper(char ch)
{
return std::toupper((unsigned char) ch);
}
static bool eq(char c1, char c2) {
return to_upper(c1) == to_upper(c2);
}
static bool lt(char c1, char c2) {
return to_upper(c1) < to_upper(c2);
}
static int compare(const char* s1, const char* s2, size_t n) {
while ( n-- != 0 ) {
if ( to_upper(*s1) < to_upper(*s2) ) return -1;
if ( to_upper(*s1) > to_upper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a) {
auto const ua (to_upper(a));
while ( n-- != 0 )
{
if (to_upper(*s) == ua)
return s;
s++;
}
return nullptr;
}
};
using ci_string = std::basic_string<char, ci_char_traits>;
int main()
{
ci_string s1 = "abcd";
ci_string s2 = "ABCD";
if ( s1 == s2 )
cout << "same" << endl;
else
cout << "not same" << endl;
}
Sequence(선형) Contianer
1. 선형 컨테이너들은 인터페이스가 동일하지만, vector는 push_front를 제공하지 않는 등 제공 유/무 여부가 다름
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <forward_list>
#include <array>
using namespace std;
int main()
{
list<int> s;
//deque<int> s;
//vector<int> s;
s.push_front(10);
s.push_back(20);
cout << s[0] << endl;
}
2. Vector
- 생성/삽입/접근
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// vector 생성
vector<int> v1;
vector<int> v2(10);
vector<int> v3(10, 3);
vector<int> v4(v3);
vector<int> v5 = {1,2,3,4};
vector<int> v6{1,2,3,4};
vector<int> v7(10,0); // 10 개를 0으로 초기화
vector<int> v8{10,0}; // 2개를 10, 0 으로 초기화
//----------------------
vector<int> v;
//v.push_front(10); // error.
v.push_back(10);
v.push_back(20);
//v.insert( 위치, 30);
v.insert( begin(v)+1, 30); // 10 30 20
// 요소 꺼내기
int n = v.front();
int n1 = v[0];
int x[5] = { 1,2,3,4,5};
v.assign( x, x+5);
//sort( begin(v), end(v));
v[100] = 10; // 예외 없이 runtime error
//v.at(100) = 10; // 예외 발생
for ( int i = 0; i < v.size(); i++)
v[i] = 10; // ok
v.at(i) = 10; // !!
}
- size와 capacity(메모리사용량)
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(10,0);
v.resize(7); // ?
cout << v.size() << endl; // 7
cout << v.capacity() << endl; // 10
v.push_back(0);
cout << v.size() << endl; // 8
cout << v.capacity() << endl; // 10
v.pop_back();
cout << v.size() << endl; // 7
cout << v.capacity() << endl; // 10
v.shrink_to_fit();
cout << v.size() << endl; // 7
cout << v.capacity() << endl; // 7
v.push_back(0);
cout << v.size() << endl; // 8
cout << v.capacity() << endl; // 컴파일러마다 다름
}
3. std::array
-
vector는 heap을 사용하지만, std::array는 일반 배열처럼 stack 사용
-
동적으로 크기 변경 불가
4. 객체 삽입시 최적화 관련
#include <iostream>
#include <vector>
using namespace std;
class Point
{
int x, y;
public:
Point(int a, int b) : x(a), y(b){ cout << "Point()" << endl; }
~Point() { cout << "~Point()" << endl;}
Point(const Point& ) { cout << "Point(const Point&)" << endl;}
};
int main()
{
vector<Point> v;
// 1.
//Point p1(10, 20);
//v.push_back(p1 );
// 2. 임시객체로 넣기
//v.push_back( Point(10,20));
// 3. 객체 자체를 vector가 만들게 한다.
// 객체를 전달하지 말고, 생성자 인자를 전달한다.
//v.emplace_back( 10, 20 ); // emplace_front()
// insert ==> emplace
Point p1(10, 20); // 객체 생성
// p1 사용...
//v.push_back(p1 );
v.push_back( move(p1) );
// p1은 이제 필요 없다.
cout << "-----------" << endl;
}
Adapter container의 원리
1. stack을 만드는 2가지 방법
- stack을 처음 부터 새롭게 구현
- list의 인터페이스를 변경해서 stack처럼 동작하도록 처리
#include <iostream>
using namespace std;
// list 가 제공된다.
#include <list>
/*
// 그런데 stack 이 필요하다.
template<typename T, typename C = deque<T> > class stack
{
C st;
public:
inline void push(const T& a) { st.push_back(a);}
inline void pop() { st.pop_back();}
inline T& top() { return st.back();}
inline bool empty() { return st.empty();}
};
*/
#include <vector>
#include <deque>
#include <stack>
int main()
{
// stack<int, list<int, MyAlloc<int>> > s3;
stack<int> s2;
stack<int, vector<int>> s1;
stack<int, list<int>> s;
s.push(10);
s.push(20);
cout << s.top() << endl; // 20
s.pop();
cout << s.top() << endl; // 10
}
- STL의 우선순위 큐, deque 등도 비슷한 방법으로 구현됨
Associative container
1. set
- STL에서 tree를 쓰는 대부분의 컨테이너는 red black tree 사용
#include <iostream>
#include <set>
#include <functional>
using namespace std;
int main()
{
//set<int> s;
set<int, greater<int> > s;
//s.push_front(10); // error
s.insert(30);
s.insert(40); // < 연산으로 비교, >
s.insert(20);
s.insert(10);
s.insert(45);
s.insert(25);
// 알고리즘의 find를 쓸 경우 선형 검색 -> 비권장, 성능 나쁨
//auto p2 = find( begin(s), end(s), 10);
//cout << *p2 << endl;
auto p2 = s.find(10); // 이진 검색.
cout << *p2 << endl;
auto p = begin(s);
//*p = 10;
while( p != end(s))
{
cout << *p << endl;
++p;
}
}
- 중복 삽입시
#include <iostream>
#include <set>
using namespace std;
int main()
{
//set<int> s;
multiset<int> s;
s.insert(30);
s.insert(40);
s.insert(20);
s.insert(10);
s.insert(45);
s.insert(25);
// set : pair
// multiset : iterator
auto ret = s.insert(20);
//pair<set<int>::iterator, bool> ret = s.insert(20);
// if ( ret.second == false )
// cout << "fail" << endl;
for( auto n : s)
cout << n << endl;
}
2. map
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
map<string, string> m;
// 1. pair 만들어서 insert
pair<string, string> p1("월요일","mon");
m.insert( p1 );
// 2. make_pair
m.insert( make_pair("화요일","tue"));
// 3. [] 연산
m[ "수요일"] = "wed";
cout << m[ "월요일"] << endl; // "mon"
cout << m[ "토요일"] << endl; // 첨자로 접근시 없으면 : make_pair("토요일", "") 항목추가
auto ret = m.find("일요일");
if ( ret == m.end())
cout << "fail" << endl;
}
3. unordered 컨테이너들
- 내부적으로 hash table 사용
- 표준에서 제공하는 hash function
#include <iostream>
#include <string>
#include <functional>
using namespace std;
int main()
{
hash<int> hi;
hash<double> hd;
hash<string> hs;
cout << hi(50) << endl;
cout << hd(3.4) << endl;
cout << hs("hello") << endl;
}