C++ STL - STL 설계 철학[2]
이번 시간에는 다음 내용을 살펴보겠습니다.
- 제너릭 알고리즘의 개념, 컨테이너와 알고리즘의 연결(iterator)
- STL의 구조와 유사한 방식으로 간략한 라이브러리 설계
- 예제
- C++17 이후의 STL 변화
STL Find algorithm
1. C언어의 strchr() 함수
- 문자열에서 문자를 검색시 널 문자('\0')를 만날 때 까지 검색(구간 종료 조건도 검색 대상에 포함)
- 검색 실패시 NULL 포인터(0) 리턴
- 개선점 -> 부분 문자열 검사 불가, 종료 조건을 정의 할 수 없음, char 타입만 검색 가능
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char s[] = "abcdefg";
char* p = strchr(s, 'c');
if (p == 0)
cout << "fail" << endl;
else
cout << "success : " << *p << endl;
}
2. strchr()의 문제점을 개선한 함수 efind 함수
- 검색 대상 -> 모든 타입의 배열
- 검색 구간 first, last(last는 검색에 포함 되지 않음 -> half open range)
- 구간의 표현 -> 포인터 뿐 아니라 객체도 가능(단 ++, *, !=, == 연산이 가능해야 함)
- 구간의 이동 -> 전위 연산 ++
- 검색 실패시 -> last 위치 리턴
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T1, typename T2>
T1 efind(T1 first, T1 last, T2 value)
{
while (first != last && *first != value)
++first;
return first;
}
int main()
{
double x[10] = { 1,2,3,4,5,6,7,8,9,10 };
double* p = find(x, x + 10, 5);
if (p == x + 10)
cout << "fail" << endl;
else
cout << "success : " << *p << endl;
}
STL Container & iterator
Container
- 데이터 저장소 Node를 갖는 연결리스트
- 위에서 만든 efind는 slist의 요소 검색 불가
#include <iostream>
using namespace std;
template<typename T> struct Node
{
T data;
Node* next;
Node(const T& a, Node* n) : data(a), next(n) {}
};
template<typename T> class slist
{
Node<T>* head = 0;
public:
void push_front(const T& a)
{
head = new Node<T>(a, head);
}
};
int main()
{
slist<int> s;
s.push_front(10);
s.push_front(20);
s.push_front(30);
s.push_front(40);
s.push_front(50);
}
Container와 알고리즘의 결합
- efind 함수 개선
- node 탐색을 위해 pointer를 전달 받는 것이 아닌, node의 주소를 순회 할 수 있는 객체를 정의 -> iterator
- iterator 덕분에 efind는 배열 뿐 아니라, 모든 선형 자료구조에 대해 선형 탐색 수행 가능
ntributor
107 lines (76 sloc) 1.61 KB
#include <iostream>
using namespace std;
template<typename T> class slist_iterator
{
Node<T>* current = 0;
public:
slist_iterator(Node<T>* p = 0) : current(p) {}
slist_iterator& operator++()
{
current = current->next;
return *this;
}
T& operator*() { return current->data; }
bool operator ==(const slist_iterator& it)
{
return current == it.current;
}
bool operator !=(const slist_iterator& it)
{
return current != it.current;
}
};
template<typename T> class slist
{
Node<T>* head = 0;
public:
void push_front(const T& a)
{
head = new Node<T>(a, head);
}
using iterator = slist_iterator<T>;
iterator begin() { return iterator(head);}
iterator end() { return iterator(0); }
};
int main()
{
slist<int> s;
s.push_front(10);
s.push_front(20);
s.push_front(30);
s.push_front(40);
s.push_front(50);
slist<int>::iterator p = s.begin();
slist<int>::iterator p2 =
efind(s.begin(), s.end(), 120);
if (p2 == s.end())
cout << "fail" << endl;
else
cout << *p2 << endl; // 20;
}
STL 구조 핵심정리
- 템플릿을 통한 불필요한 함수 구현을 줄임
- 자료구조와 알고리즘이 분리된 라이브러리
- 알고리즘 함수는 자신이 어떤 자료구조에 대해 연산을 수행하는지 모름
STL 구조와 특징
Member Type
- 모든 stl 컨테이너는 value_type을(자료형의 타입) 멤버로 갖도록 설계됨
- size_type, iterator, pointer .. 등등 여러 메타정보를 멤버로 갖고 있음
- 컨테이너의 타입을 출력하는 함수 예제
template<typename T>
void print(T& c)
{
typename T::value_type n = c.front(); // double
cout << n << endl;
}
int main()
{
list<double> v = { 1,2,3 };
print(v);
}
C++17과 STL
- C++17 이후 컨테이너 사용시 템플릿 인자 생략 가능
#include <iostream>
#include <list>
using namespace std;
template<typename T> class List
{
public:
List(int sz, T initValue) {}
};
int main()
{
List<int> s1(10, 0);
List s2(10, 0); // C++17 ok.
list<int> s3 = { 1,2,3 };
list s4 = { 1,2,3 }; // C++17 style
}
Concept (C++20 이후)
- 특정 함수(클래스)를 사용하기 위해 타입이 가져야하는 조건을 코드에 명시 가능
- 함수 전달 인자에 제약 조건을 명시 할 수 있음
- 템플릿 작성시 모든 타입이 아닌, 조건을 만족하는 타입에 대해서 동작하도록 설계 가능