본문 바로가기
Programming/Tips(C++,C#)

Chapter 2-6. 내용정리

by 곰네Zip 2014. 10. 30.

1. 템플릿

   - 임의의 데이터 타입을 위해 작성된 함수 or 클래스. 정확하게는 '정의'만 한 것이다. 예를 들어 다음과 같은 템플릿이 있다고 하자. 

 template <class T>

inline const T& max(const T& a, const T& b)

{

    return a>b? a: b;

}

 위 정의 자체는 아무런 영향을 주지 않는다. 이 정의가 유효하려면, 사용이 되어야 '컴파일타임'에 실제 함수가 만들어진다.

 예) 

 int a = 10;

 int b = 10;

 max(a,b); // 위 템플릿에서 max(T, T). 여기서는 타입이 int이므로, max(int, int)함수가 생성된다.

 float c = 10.1f

 max(a,c); // 찾아봐도 max(T1, T2)는 존재하지 않는다. 컴파일러시 T가 모호하다고 에러를 준다.

 만약 T1, T2를 지원하고 싶다면 다음과 같이 작성하여도 동작은 한다. 

  하지만 이렇게 작성하지 말고, 멤버함수를 만들어서 처리할 수 있다. 멤버함수도 템플릿이 될 수 있다. 

   위의 경우 컴파일 시, 경고가 발생한다. 지금 x.getvalue()에서 묵시적인 형 변환이 일어났다. 그러면서 복사본이 만들어 졌는데, 그 복사본은 assign의 범위를 가지기에 경고를 표시하여 준다.

 

2. 형변환 (casting)

 -형변환에는 4가지가 있음.: static_cast, dynamic_cast, const_cast, reinterpret_cast 

   1) const_cast

     const변수의 속성을 할당/제거할때 사용한다.

   2) static_cast

     기존 C스타일 캐스팅과 동일한 동작.

   3) dynamic_cast

     다형성을 띄고 있는 타입을 실제타입으로 변환(downcast)하는 것을 가능하게 함. 단, 다형성을 가지지 않는 객체간 변환은 불가능하다. (virtual 함수로 상속받은 함수가 필요하다.) 그리고 RTTI옵션이 체크해제되어있으면 캐스팅 구문을 만났을 때, 프로그램이 비정상 종료된다.

( RTTI옵션 설정 방법은 옆의 링크 참조 : http://msdn.microsoft.com/ko-kr/library/we6hfdy0.aspx)

 dynamic_cast가 더 안전한 변환을 제공하지만 포인터나 참조에서만 사용이 가능하다. (MSDN내용 발췌 :  http://msdn.microsoft.com/ko-kr/library/c36yw7x9.aspx )


    4) reinterpret_cast

   지정된 포인터를 정한 캐스팅 방식으로 다시 읽어버리는 캐스트 방식. 안정성을 보장할 수 없으므로 변환관계가 명확할 때에 사용하는 것이 좋다. 하지만 가급적 사용을 자제하는 것을 권장함.

3. 예외처리

 예외가 발생하면 exception이 발생한다. (또는 프로그래머가 throw를 통해 excpetion을 던져줄 수 있다.) 예외가 발생하면 catch문을 만나거나 main함수를 빠져나갈 떄 까지, stack unwinding을 수행한다.

 예외를 지정하여줄 수 있다. (exception specification). 예외를 지정하면 어느 예외가 던져질 지, 알 수 있다. 하지만 지정되지 않은 예외가 발생할 경우. terminate가 호출되어 프로그램은 종료된다. 해당 함수에서만 던져질 수 있는 예외를 지정하는 것이 아니라, try~catch구문에서 다른 함수가 호출되면, 그 함수에서 던져질 수 있는 예외도 고려해야 한다. 

   STL의 경우 기본적으로 안정성 보다는 성능 위주로 고려되었다. 그래서 상대적으로 안정성에 대한 요구가 발생되었고, 그에 따라 STL에서는 기본적으로 노드를 구성/변경하는데 있어 성공하거나 영향을 받지 않도록 설계되었다.


4. auto_ptr

  스마트포인터의 한 종류. auto_ptr이 제거되면 가리키고 있는 대상객체도 메모리에서 해제된다. (소멸자를 호출하여준다.) auto_ptr은 소유권 개념이 있고, 소유권은 공유되지 않는다. 그래서 할당과정은 소유권을 이전하는 개념이다. 그래서 멤버변수로 사용될 경우. 복사생성자나 할당연산자를 지원하지 않는 클래스에서 사용가능하다. (복사나 할당시 소유권을 넘겨주므로) 또한, 레퍼런스 카운트가 없다. 

  레퍼런스 카운팅을 지원하고, 컨테이너에 넣을 수 있는 스마트포인터는 tr1::shared_ptr, boost::shared_ptr, Loki::SmartPtr등이 있다. (http://gomnezip.tistory.com/316에 해당 내용 있음)


* STL컴포넌트 용어정리

   컨테이너 : 특정 타입의 원소들의 집합을 다루는데 사용함

   반복자 : 객체가 소유한 원소를 순회하기 사용한다.

      -> 반복자는 모든 컨테이너에 대해 동일한 인터페이스를 제공한다.

   알고리즘 : 객체가 소유한 원소를 처리하기 위해 사용.

      -> 각 객체를 순회하는데 있어 반복자가 사용되었다. 모든 컨테이너에 대해 공통적인 인터페이스를 제공하므로, 반복자를 사용하면 알고리즘은 하나만 존재하여도 된다.

5. STL 컨테이너

  1) 컨테이너의 특징

    - '값 의미론'을 제공 : 컨테이너에 원소를 삽입시, 복사본을 만들어서 가지고 있다.

    - 모든 원소는 순서를 가짐 : 이 순서는 '반복자'를 이용하여 순회할 수 있다.

    - 컨테이너의 종류

       i) 시퀀스 컨테이너 : 순서를 가지는 컨테이너. list, vector, deque등이 있음

       ii) 연관 컨테이너 : 이진트리로 구성된 컨테이너. set/multiset/map/multimap이 있다.

 

  2) 공통적인 동작

    - 복사생성자, 소멸자, 기본생성자등을 제공한다.

    - 비교동작 : 일반적인 비교연산자를 사용한다. 그리고, 컨테이너의 타입이 같아야 하고, 사전방식의 비교를 사용하여 검색한다. ( 'a'와 'aa'를 비교하면 'a'가 더 작다.)

    - 사용자가 컨테이너를 할당시, 컨테이너에 원소들을 복사한다. 같은 타입의 원소를 사용중이고, 소스가 더 사용되지 않으면 swap을 이용하자. (swap은 상수복잡도를 보장한다. 즉, 시간이 덜 소요된다.)

   

  3) vector

    - 벡터의 메모리 구조

    - 동적배열을 가지는 컨테이너. 순서를 가지는 컬렉션이다. 랜덤액세스( []연산 )를 제공하고, 리스트의 제일 뒤에 삽입/삭제는 매우 빠르다. 그러나 중간에 삽입/삭제시 이보다 꽤 시간이 많이 소요되었다.

   

 

   4) deque

    - vector와 비슷하지만 양방향에 대하여 개방되어 있다.

     dequq의 메모리 구조

 

     위와 같이 구성되어 있어, 재할당시 벡터보다는 효율적이다. (메모리 전체를 복사하는 것이 아니라 일부만 재할당 하면 된다.) 그리고, 원소의 엑세스와 반복자 이동시에는 벡터보다 조금 느리다.

 

 5) 리스트

   - 리스트 구성 방식

 

   - 이중 연결 리스트처럼 구성되어 있다. 랜덤 액세스는 지원하지 않는다. 그러나 삽입/삭제의 동작은 vector나 deque에 비해 빠르다. (복사하는 과정 없이 포인터 값만 조작한다.)

 

 6) set/multiset

   - 연관컨테이너(원소가 insert시 자동으로 정렬하는 컨테이너)임. 따라서, 직접적으로 원소에 접근하여 값을 수정할 수 없다. (직접 값을 수정할 경우 자동정렬이 깨어진다.)

   - set과 multiset의 차이는, 중복 허용안함(set)과 중복허용(multiset)의 차이.

   - 정렬조건에 대해 다음 두가지 방법으로 설정 가능하다.

     i) 템플릿의 파라미터로 넣는 방식

       예) set<int, greater<int> > col; //여기서 주의할 것은 'greater<int> >'에서 '>'와 '>'사이를 하나 띄어줄것. (아니면 컴파일 타임에 에러가 발생한다.)

     ii) 생성자의 파라미터로 넣는 방식

       예) set c(op); op는 이항조건연산자로, 비교하는 조건이 들어가면 된다.

   *정렬조건을 별도로 설정하지 않으면 less<type>이 기본으로 선택된다.

   - set/multiset에서 제공하는 특별한 검색 함수는 다음과 같다.

       lower_bound, upper_bound, equal_range

 

  다음은 set을 사용한 예제

   

 set의 경우 정렬기준을 런타임시에 변경 가능하다. 다른 정렬기준을 가진 컨테이너를 생성 후, 할당하면 된다.

  * 함수명 뒤에 const를 붙이게 되면 해당 멤버함수가(멤버함수에만 const키워드 붙을 수 있음) 해당 객체의 멤버를 수정하지 않음을 의미함.

 

  7) map/multimap

     -set/multiset과 유사하다. 차이점은 <key,value>의 한 쌍으로 동작한다는 것이다. 따라서, key값은 수정이 불가능하지만, value값은 수정 가능하다. 그리고 map의 경우 key값의 중복을 허용하지 않고, multimap의 경우 key값의 중복을 허용한다.

   map사용예제

 

  8) 다른 STL컨테이너

   - 사용자가 직접 작성한 컨테이너나, 기본의 배열, 스트링도 STL의 컨테이너처럼 사용가능

    i) 침습적(invasive)방법

      - 사용자가 STL이 필요로 하는 인터페이스를 제공하여 주면 됨

    ii) 비침습적(noninvasive)방법

      - 알고리즘과 컨테이너의 상호 인터페이스로 사용가능한 인터페이스를 작성하거나 제공. 예를들어, 배열의 경우 begin, end가 없으면 포인터 같은 대체 반복자를 제공

    iii) wrapper방법

     - 위 두 바업을 결합하여 STL컨테이너와 유사한 인터페이스를 제공하며, 내부 데이터는 은닉하는 클래스 작성

     래퍼클래스 작성 예제

 

 해당 클래스 사용 예제

 

 9)레퍼런스 의미론 구현

   - 기본적으로 STL컨테이너는 원소의 값을 복사하여 가지고 있는 '값 의미론'을 지원한다. 하지만, 복사에 시간이 많이 소요될 경우. 원소의 값을 복사하는 것이 아니라 주소를 가지고 있으면 된다. 이를 위해서는 스마트 포인터를 사용하는것이 좋다. 그러나 auto_ptr은 소유권 관련한 문제가 있어, 컨테이너에 사용할 수 없다. 그래서 이를 지원하는 스마트 포인터를 사용하여야 한다.

 사용 예제

  * 언제 어느 컨테이너를 사용할 것인가?

   1) 기본적으로 벡터를 사용한다. 벡터는 간단한 내부 자료구조를 사용하며 랜덤 액세스를 지원한다.

   2) 만약 원소를 컨테이너의 앞/끝에 자주 삽입/제거를 한다면 반드시 deque를 사용할 것. 또한 원소 제거

     시 컨테이너의 메모리 사용량이 줄어들어야 할 때에도 deque를 사용할 것

   3) 원소의 제거/삽입/이동이 컨테이너 중간에서 빈번할 경우 list를 고려해볼 것. list의 경우 삽입 삭제가

     빠르지만, 원소 액세스시 성능상에 제약을 받음

   4) 예외에 대해서 각각의 동작이 성공하거나 아무런 영향도 가지지 않는 컨테이너가 필요하다면 list나 연

     관컨테이너를 고려할 것

   5) 특정기준에 따라 원소를 자주 검색해야 하면 set/multiset을 사용할 것.

   6) key/value쌍을 위해서는 map/multimap을 사용할 것

   7) 연관배열이 필요하다면 map을 사용할 것

   8) 사전이 필요하다면 multimap을 사용할 것

     * 해시 테이블도 고려해볼만 함. 그러나 해시 컨테이너는 순서가 없으므로, 순서에 의존이 필요하다면 사

     용하지 않는편이 좋음

 

 

반응형

댓글