★ STL 종류
1. 컨테이너
→ 배열과 같이 여러개의 값을 저장할 수 있는 구성단위. 동질적 같은 종류의 값을 저장할 수 있으며, 컨테이너도 vector, list, deque, map, queue ... 등등으로 종류가 다양하다.
2. 알고리즘
→ 배열을 소트하거나 리스트에서 특정 값 검색과 같은 특별한 작업을 수행하기 위한 방법
3. 이터레이터
→ 포인터의 일반화 버전. 이것은 포인터 같이 컨테이너 안에서 지시하는 위치를 옮기고 또 다른 원소를 가리키며, 이 것이 가리키는 객체 혹은 변수(이것을 원소라고 한다.)의 값을 갱신 혹은 삭제 등을 할 수 있게 하는 존재다.
4. 함수 객체
→ 이것은 클래스 객체일 수도 있고 함수 포인터가 될 수도 있다.
★ STL은 배열, 큐, 리스트 같은 다양한 컨테이너를 생성할 수 있게 해주며, 그것들에 대해 검색, 소팅, 무작위배치 등등의 다양한 작업을 손쉽게 수행할 수 있게 해준다.
★ vector 템플릿 클래스
→ 배열과 매우 흡사한 컨테이너의 한 종류 (하지만 이것의 공간할당은 힙에서 이루어진다)
이것의 특징은 각각 원소들에 대해서 임의접근(Random Access)이 가능하다는 점이다.
* 벡터 컨테이너의 생성
→ 벡터 컨테이너를 만들기 위해서는 우선 vector를 인클루드 시켜야 한다.
#include <vector>
→ 또한 vector는 C++ 표준이기 때문에 std 네임스페이스에 들어있다는 것을 잊어서는 안된다.
using namespace std;
등으로 std:: 없이 vector란 용어를 쓸 수 있다.
→ 생성 방법은 이렇다.
vector<int> ratings( 5 ); // 5개의 int 타입값을 가지는 벡터
→ 생성자를 쓰지 않으면 공간이 없는 벡터를 만들게 된다. 그렇다 하더라도 push_back()등의 메서드를 통해서 벡터 공간을 늘림과 동시에 값을 할당 할 수도 있다. 위에서는 인수가 5라는 상수가 적혀 있지만 인수를 변수로 넣어도 무방하다. 또한 여기서는 데이터형 인수를 int로 넣었으나, long, float 등의 다른 표준타입도 가능하고, 사용자 정의 클래스 또한 가능하며 심지어는 포인터 타입을 데이터형 인수로 넣을 수도 있다.
* 벡터 컨테이너 원소의 접근
→ 벡터는 임의 접근이 가능하기 때문에 []연산자를 통해서 벡터의 일정 위치 값에 대한 접근이 가능하다.
ratings[3] = 9;
for ( int i = 0; i < 5; i++ )
cout << ratings[i] << endl;
* 이터레이터를 통한 원소 접근
→ STL에서 포인터와 비슷하 이터레이터를 이용하여 원소에 접근이 가능하다. 이터레이터는 벡터 뿐만 아니라 거의 모든 STL 컨테이너에 대해서 사용이 가능하다. 선언은 아래와 같이 한다.
vector<double>::iterator pd;
→ double형 벡터에 대한 이터레이터 pd를 선언하였다.
이제 pd에 벡터 컨테이너 원소를 가리키게 해야 한다.
vector<double> scores( 5 );
pd = scores.begin();
*pd = 5.0;
++pd;
→ 5개의 double 타입 원소를 가지는 벡터 scores를 생성하고,
begin() 메서드를 써서 pd가 scores의 가장 첫번째 원소를 가리키도록 했다.
begin() 은 STL의 대부분의 컨테이너에 있는 메서드로써 이것은 원소 중 가장 첫번째 것에 대한 이터레이터를 리턴한다. 물론 리턴된 값을 받는 이터레이터와 리턴하는 이터레이터는 데이터형 인수 (위에서는 double)가 반드시 일치해야 한다.
*pd를 봐서는 이 연산자는 pd가 가리키는 원소 값을 나타낸다는 것을 금방 알 수 있을 것이다. 그리고 ++pd를 통해 pd가 다음 원소를 가리키게 한다는 것 또한 알 수 있다.
→ begin() 메서드도 있지만 end() 라는 메서드도 있다. 이것은 컨테이너의 가장 마지막 원소의 다음 지점에 대한 이터레이터를 리턴한다.
이것들과 for문을 이용해서 다음과 같은 것도 가능하다.
for ( pd = scores.begin(); pd != scores.end(); ++pd )
cout << *pd << endl;
→ pd를 scores의 처음부터 끝까지 돌게 해서 가리키는 값을 출력하는 문이다.
위의 것 대신 아래와 같이 해서 약간이나마 속도 향상을 시킬 수 있다.
vector<double>::iterator endpd = scores.end();
for ( pd = scores.begin(); pd != endpd; ++pd )
cout << *pd << endl;
→ for문에서 end() 의 호출을 반복적으로 쓰지 않게 함으로써 약간이나마 속도를 향상시킬 수 있다.
* 원소 추가하기
→ 마땅히 컨테이너에 공간이 없다고 하더라도 push_back() 메서드를 통해 원소를 추가할 수 있다. 이 메서드는 벡터 컨테이너의 끝에 공간을 하나 더 확보하고 원소를 추가해서 크기를 늘린다.
double temp = 3.0;
scores.push_back( temp );
→ 벡터의 맨 뒷부분에 3.0 값을 가지는 변수를 추가한다.
→ STL에서는 push_front() 라는 메서드도 있다. 이것은 컨테이너의 맨 앞에 원소를 추가하는 메서드인데 벡터는 이 메서드가 없고 list 등의 다른 컨테이너에 존재한다.
insert() 라는 메서드도 있다. 이것은 또 다른 벡터 컨테이너의 지정된 범위를 삽입하고자 하는 원하는 위치에 통으로 삽입한다.
vector<double> scores( 5 );
vector<double>::iterator it, endit;
endit = scores.end();
double fi = 0.0;
for ( it = scores.begin(); it != endit; ++it )
{
*it = fi;
fi += 1.0;
}
// 이 시점에서 scores원소는 { 0.0, 1.0, 2.0, 3.0, 4.0 }
vector<double> kimscores( 3 );
endit = kimscores.end();
fi = 10.0;
for ( it = kimscores.begin(); it != endit; ++it )
{
*it = fi;
fi += 0.5;
}
// 이 시점에서 kimscores 원소는 { 10.0, 10.5, 11.0 }
kimscores.insert( kimscores.begin() + 1, scores.begin(), scores.begin() + 3 );
// 이 시점에서 scores는 그대로이지만
// kimscores에서는 scores의 일부가 삽입되어
// { 10.0, 0.0, 1.0, 2.0, 10.5, 11.0 } 이 되었다.
→ insert()의 인수는 이렇다.
인수1 : 삽입하고자 하는 원소 위치. 인수1의 이전위치에 삽입된다. 만약 인수1 이...begin()이면 처음 위치에 삽입되고, ...end() 이면 끝 부분에 삽입된다. 중요한 것은 반드시 메서드를 호출한 컨테이너에 대한 이터레이터를 넣어야 한다는 것이다.
인수2, 3 : 삽입할 대상범위를 이터레이터로 나타낸다.
[인수2, 인수3) 의 범위로써 인수 2를 포함한 부분부터 인수3을 제외한 그 이전까지이다.
* 원소 삭제하기
→ erase() 메서드를 통해 원소를 제거할 수 있다. 제거된 원소는 그것에 대한 공간까지 해제된다.
erase()는 두가지의 오버로딩 버전이 있다.
1. 하나의 원소만을 삭제하는 버전 ( 인수 1개 )
인수1 : 삭제하고자 하는 부분의 이터레이터. 이것은 erase()를 호출하는 벡터 컨테이너의 이터레이터와 동일해야 한다.
2. 여러개의 원소를 범위 지정하여 삭제하는 버전 ( 인수 2개 )
인수1, 2 : [인수1, 인수2) 의 범위로 (인수2 미만) 그것이 가리키는 원소들을 제거한다.
→ erase()의 가장 주의점은 for문인데, 이터레이터를 사용한 for문 내에서 이 함수를 쓸시 이 함수 호출 이후 반드시 갱신시켜야 할 내용이 있다.
endit = scores.end();
for ( it = scores.begin(); it != endit; ++it )
{
if ( *it < 0.0 )
scores.erase( it );
}
// 잘못된 방법
→ erase() 호출 이후 이제 it는 모르는 값을 가리키게 된다. (컴파일러에 따라 다를 수도 있지만 분명한건 VS2005에서는 런타임 에러를 유발한다.) 다음 for문 순서에 진입시 런타임 에러를 유발할 수 있다.
여기서 erase()의 리턴값에 주목해야 한다. 이것의 리턴값은 지운 값 바로 다음 순서의 이터레이터가 된다. (지운것이 마지막 원소일 시에는 scores.end()가 리턴됨 ) 그러므로 이와 같이 바꾸면 버그를 해결할 수 있다.
endit = scores.end();
for ( it = scores.begin(); it != endit; )
{
if ( *it < 0.0 )
{
it = scores.erase( it ); // erase() 이후 it가 다음것을 가리키게 되므로 it를 증가시키지 않는다.
endit = scores.end(); // 하나의 원소를 없애면 endit도 바뀔수 있으므로 (vector의 경우) 이것도 갱신한다.
}
else
++it;
}
// 올바른 방법
→ 원소를 없애는 또 다른 메서드인 clear()가 있다. 이것은 해당 컨테이너의 모든 원소를 없애는 메서드이며 매개변수가 없다.
scores.clear(); // 모든 원소가 다 지워짐.
* 그 밖의 메서드들
→ size()는 컨테이너에 있는 원소들의 개수를 리턴한다.
→ random_shuffle()은 두 개의 이터레이터 사이에 있는 원소들을 랜덤으로 재배치시킨다. 이것은 컨테이너의 멤버 함수가 아닌 일반형태의 함수이며, 벡터 뿐만 아니라 다른 컨테이너에서도 적용이 가능하다.
인수 1, 2 : 재배치하고자 하는 값들의 범위를 이터레이터로 명시한다. [인수1, 인수2) 범위의 원소들이 랜덤배치된다.
random_shuffle( scores.begin(), scores.end() );
// 이것은 scores의 멤버함수가 아니며, 이 함수는 <algorithm>에 정의되어 있다.
// [scores의 처음, scores의 끝) 범위에 있는 원소들이 랜덤 배치 된다.
→ for_each() : 이 함수 또한 <algorithm>을 인클루드 해야 하며 컨테이너의 멤버가 아니다. [인수1, 인수2) 범위의 각각의 원소에 대해서 인수3의 사용자 정의 함수를 적용시킨다. 이 함수는 원소들의 값을 변경해서는 안된다. ( 즉 사용자 정의 함수의 매개변수가 상수이어야 한다. )
인수3 : [인수1, 인수2) 범위의 각각의 원소들에 대해 적용시킬 함수 포인터. 이 함수는 (아마도) 리턴값이 없어야 하며 매개변수는 상수이고, 인수1,2가 가리키는 원소의 타입과 동일해야 한다.
void Test( const double & arg )
{ 원소들에 대한 처리를 한다. }
// 리턴타입은 없다. 매개변수는 하나이며, 상수형 참조가 되며, scores가 가리키는 타입(double)과 동일해야 한다.
...
for_each( scores.begin(), scores.end(), Test );
// Test : 위의 함수 포인터
→ for_each() 함수 대신 이렇게 쓸 수도 있다.
vector<double>::iterator it, endit;
endit = scores.end();
for ( it = scores.begin(); it != endit; ++it )
{
Test( *it );
}
→ sort() : 이것도 <algorithm>에 정의되어 있으며 컨테이너 멤버가 아니다. 이 함수는 컨테이너 원소들을 특정조건에 맞게 소팅(정렬)하는 함수로써 두가지 버전이 있다.
1. 인수가 두개인 버전 → 인수 1, 2에 범위를 지정하는 이터레이터를 넣게 되며 [인수1, 인수2) 의 범위에 대해서 소팅한다. 소팅기준은 이터레이터가 가리키는 타입의 < 연산자이며 만약 이터레이터가 가리키는 타입이 사용자 정의 클래스이면 < 연산자를 따로 정의해야 한다.
ex) 데이터형 매개변수가 Review 타입인 벡터에 대해 소팅을 적용하고 싶을때
bool operator < ( const Review & lhs, const Review & rhs )
{
... // 조건에 따라 rhs가 크면 true, rhs가 작으면 false를 리턴하게 함.
}
...
vector<Review> Books;
...
sort( Books.begin(), Books.end() );
// < 연산자 기준에 따라 소팅(sorting)을 실시함.
2. 인수가 세개인 버전 → 위의 것과 흡사하나 인수 3에 함수 포인터가 들어감으로써 소팅기준 함수를 < 연산자 정의 대신 쓰게 된다. 인수 3의 함수는 < 연산자 함수의 형태와 동일하게 bool 타입을 리턴하고, 좌변-우변 순서의 상수형 원소 타입의 참조 두개가 매개변수로 지정되어야 한다.
'컴퓨터공학 기초 > C.C++' 카테고리의 다른 글
STL - 시퀀스 컨테이너 (0) | 2011.09.16 |
---|---|
STL - 이터레이터(iterator) (0) | 2011.09.16 |
자주 사용하는 C++ STL algorithm 함수 정리 (0) | 2011.09.16 |
[DLL] DLL 만드는 방법 2 (0) | 2011.09.08 |
[DLL] DLL 만드는 방법 1 (0) | 2011.09.08 |