'STL'에 해당되는 글 2건

Posted on 2010/09/25 23:58
Filed Under ProgrammingTip

임의의 배열을 생성하여 vector를 초기화하는 코드는 아래와 같다.
// 배열을 한 개 생성하여, 배열로 vector를 초기화한다.
bool bArrary[] = {true, true, true, true, false};

vector<bool> vecArray( bArrary, bArrary+sizeof(bArrary)/sizeof(bArrary[0]) );

참 간단하다. 실제로 위의 코드가 작동되는 원리를 파악해 보자.


# 설  명
vector.h 파일을 살펴보면 vector를 초기화하는 코드 중에 다음과 같은 생성자가 있다.
template<class _Iter> vector(_Iter _First, _Iter _Last)
{
    // 이하 생략
    ...

인자 값으로 특정 STL 컨테이너 반복자(iterator)의 시작 위치와 끝 위치를 넣어 데이터를 담을 수 있다. 예를 들어 a라는 vector 컨테이너의 데이터를 vecArrary라는 또 다른 컨테이너에 전부 넣어 초기화를 하고 싶다면 다음과 같이 처리할 수 있다.
vector<bool> vecArrary( a.begin(), a.end() ); // vecArrary를 a 요소 값으로 담는다.

그렇다면 반복자라는 것은 무엇일까?

반복자 iter가 있다고 가정했을 때, 흔히 컨테이너를 순회하려면 ++iter 와 같이 ++연산자를 사용하여 현재 반복자가 어느 데이터를 가리키고 있는지 알 수 있다. 이처럼 반복자를 증가시켜 현재 가리키고 있는 데이터의 위치를 알 수 있듯이, 1컨테이너 안의 데이터들은 메모리 블록에 차례대로 쌓아놓게 되는 것이다. 즉, 반복자는 특정 컨테이너에서 해당 데이터의 메모리 위치를 가리키게 된다.

마찬가지로 배열 또한 데이터를 메모리 블록에 차례대로 쌓아놓아, 배열의 시작 주소부터 끝 주소까지 넣으면 그 값으로 컨테이너를 생성하게 되는 것이다.


# 정  리
사용자 삽입 이미지
배열과 vecotr 의 데이터는 메모리에 차례대로 위치한다.

vector를 생성할 때 초기화 함수 vector(_Iter _First, _Iter _Last) 의 값으로 데이터의 시작 주소와 끝 주소를 넣어주면 해당 데이터를 vector에 담게 된다. 마찬가지로 동일한 Sequence container인 배열 또한 시작 주소와 끝 주소를 넣어서 데이터를 vector에 담을 수 있다. 배열과 vector의 데이터가 메모리 주소값에 차례대로 있어서 가능한 일이다.


# 참고자료
위키피디아의 STL(Standard Template Library) 정보
다양한 기능을 제공하는 C++ 확장 라이브러리 Boost
크리에이티브 커먼즈 라이센스
Creative Commons License
  1. 여기서 알 수 있는 사실은 컨테이너에 데이터를 삽입하는 과정은 성능에 영향을 준다는 것이다. 컨테이너의 데이터들이 주소 값을 차례로 가지고 있어서 컨테이너의 최대 사이즈가 변경되면 알맞은 공간에 주소 값을 재할당하여 속도가 저하된다. 그래서 미리 최대 사이즈를 할당해 놓는 reserve() 함수를 사용하곤 한다.
2010/09/25 23:58 2010/09/25 23:58

Posted on 2008/12/01 02:01
Filed Under ProgrammingTip

STL Map은 시퀀스 컨테이너(Sequence container)처럼 [ ] 연산자를 제공합니다. 중요한 점은 "C++ Standard Library 튜토리얼·레퍼런스(인포북, 니콜라이 M. 조슈티스)" 책에도 나와 있듯이 [ ] 연산자의 인덱스는 정수 값이 아니라 map의 key 이라는 것입니다. 이것을 정확하게 숙지하지 않고 잘못사용하면, 심각한 문제가 생길 수 있습니다. 아래는 [ ] 연산자를 잘못 사용한 코드의 예입니다.
// 커피 자판기를 하나 만들고, 판매할 커피를 추가합니다.
std::map<int, std::string> mapCoffeeMachine;

mapCoffeeMachine[0] = "Espresso";
mapCoffeeMachine[1] = "Macchiato";
mapCoffeeMachine[2] = "ConPana";
mapCoffeeMachine[3] = "Latte";
mapCoffeeMachine[4] = "Cappuccino";
mapCoffeeMachine[5] = "Mcha";
mapCoffeeMachine[6] = "Caramel";
mapCoffeeMachine[7] = "Americano";

// 생각을 해보니 라떼와 카푸치노는 제고가 없네요.
// 라떼와 카푸치노 버튼을 자판기에서 사용하지 않습니다.
mapCoffeeMachine.erase( 3 );
mapCoffeeMachine.erase( 4 );

...

// 한 구매자가 자판기 앞에서 돈을 넣고 아메리카노를 누릅니다.
for ( int i = 0; i < mapCoffeeMachine.size(); i++ )
{
    if ( mapCoffeeMachine[i] == "Americano" )
    {
        ... // 따끈따끈한 아메리카노 한잔이 나옵니다.!?
    }
}

과연 구매자는 자판기에서 따뜻한 아메리카노 한잔을 뽑아 담배와 함께 커피한잔을 할 수 있을까요? 평생을 기다려도 아메리카노는 나오지 않습니다. 위의 코드는 [ ] 연산자의 인덱스를 정수 값으로 취급한 상당히 잘못된 코드입니다. 문제점은 크게 두 가지로 나뉠 수 있는데, 첫 번째는 size() 함수로 컨테이너의 크기를 구하면 제거된 3, 4번 키 값 때문에 사이즈가 6 이 됩니다. 그러므로 6, 7번 key 값은 아예 비교 대상이 안 됩니다. 커피를 마시러 온 사람은 메뉴를 보고 눌렀는데, 커피는 않나오고 동전이 반환된 꼴이 된 것입니다. 그러나 커피가 않나오는 것보다 더 큰 문제점이 있습니다. 바로 제거한 3, 4번 키 값의 내용이 == 동등 연산자를 통해 빈 value 값이 다시 추가가 된다는 점입니다. 컨테이너의 크기는 8이 되어 이제 커피메뉴에는 8개로 등장하는데, 아무것도 없는 메뉴가 자판기에서 눌러질 수 있다는 사실입니다. 커피가 들어있지 않는 빈 컵만 받게 되겠지요.

위에는 단순히 자판기로만 예를 들었지만 삭제한 내용이 다시 추가되는 문제점은 실제 개발 시에 큰 문제점을 가져올 수 있습니다. 특히 여러 명의 개발자가 작업을 할 경우 개발자 마다 서로 다른 작업방식으로 프로그램이 다운될 수도 있습니다. 이러한 문제점 때문에 컨테이너를 순회할 때는 반드시 반복자를 사용하는 게 필요합니다.



포이트리 로고
직원 할인으로 점심식사 후 거의 매일가는 서초동 포이트리 커피숍 :)
크리에이티브 커먼즈 라이센스
Creative Commons License
2008/12/01 02:01 2008/12/01 02:01

About

by 외계고양이

Counter

· Total
: 82629
· Today
: 24
· Yesterday
: 53