언어/C++

[Effective C++] 예외를 던지지 않는 swap에 대한 지원도 생각해 보자

2020. 1. 25. 22:52
반응형

항목 25 : 예외를 던지지 않는 swap에 대한 지원도 생각해 보자

 

swap는 자기대입 현상의 가능성에 대처하기 위한 대표적인 메커니즘으로서 널리 사랑받아 왔습니다.

두 객체의 값을 '맞바꾸기'한다는 것은 각자의 값을 상대방에게 주는 동작입니다.

표준 swap가 동작시 복사가 세 번 일어납니다. 이런 복사가 일어날시 손해를 보는 타입들 중 으뜸을 꼽는다면 아마도 다른 타입의 실제 데이터를 가리키는 포인터가 주성분인 타입일 것입니다.

 

이러한 개념을 쓰고 있는 기법이 바로 pImpl입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class WidgetImpl {
public :
    ...
 
private :
    int a, b, c;
    std::vector<double> v;
    ...
};
 
class Widget {
public:
    Widget(const Widget& rhs);
 
    Widget& operator=(const Widget& rhs)        // Widget을 복사하기 위해, 자신의 WidgetImpl 객체를 복사합니다.
    {
        ...
        *pImpl = *(rhs.pImpl);
        ...
    }
    ...
 
private :
    WidgetImpl * pImpl;                            // Widget의 실제 데이터를 가진 객체에 대한 포인터
};
Colored by Color Scripter
 

 

이렇게 만들어진 Widget 객체를 우리가 직접 맞바꾼다면, pImpl 포인터만 살짝 바꾸는 것 말고는 실제로 할 일이 없습니다. 그래서 조금 손을 보고 싶습니다. std::swap에다가 뭔가를 알려 주는거죠. Widget 객체를 맞바꿀 떄는 내부의 pImpl 포인터만 맞바꾸라고 말입니다. 기본 아이디어만 코드로 보여드리겠습니다. 아직 컴파일은 되지 않습니다.

 

1
2
3
4
5
6
7
8
namespace std {
 
    template<>
    void swap<Widget>(Widget& a, Widget& b)            // 이 코드는 T가 Widget일 경우에 대해 std::swap을 특수화한 것입니다.
    {
        swap(a.pImpl, b.pImpl);
    }
}
Colored by Color Scripter
 

 

함수 시작 부분에 잇는 'template<>'가 std::swap의 완전 템플릿 특수화 함수라는 것을 컴파일러에게 알려 주는 부분입니다. 일반적으로 std 네임스페이스의 구성요소는 함부로 변경하거나 할 수 없지만, 프로그래머가 직접 만든 타입에 대해 표준 템플릿을 완전 특수화하는 것은 허용이 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Widget {
public:
    ...
    void swap(Widget& other)
    {
        using std::swap;
 
        swap(pImpl, other.pImpl);
    }
    ...
};
 
namespace std {
    template<>
    void swap<Widget>(Widget& a, Widget& b)
    {
        a.swap(b);
    }
}
Colored by Color Scripter
 

 

이렇게 작성하면 컴파일될 뿐만 아니라, 기존의 STL 컨테이너와 일관성도 유지되는 착한 코드가 되었습니다.

 

함수 템플릿을 부분적으로 특수화하고 싶을수도 있습니다. 하지만 C++는 클래스 템플릿에 대해서는 부분 특수화를 허용하지만 함수 템플릿에 대해서는 허용하지 않도록 정해져 있습니다.

 

함수 템플릿을 '부분적으로 특수화'하고 싶을 때는 오버로드 버전을 하나 추가하면 됩니다.

일반적으로 함수 템플릿의 오버로딩은 해도 별 문제가 없지만, std는 조금 특별한 네임스페이스이기 때문에 이 네임스페이스에 대한 규칙도 다소 특별합니다. std 내의 템플릿에 대한 완전 특수화는 OK이지만, std에 새로운 템플릿을 추가하는 것은 OK가 아닙니다.

 

우리는 swap을 호출해서 우리만의 효율 좋은 '템플릿 전용 버전'을 쓸 수 있으면 좋겠습니다. 이를 위한 방법은 간단합니다. 멤버 swap를 호출하는 비멤버 swap를 선언해 놓되, 이 비멤버 함수를 std::swap의 특수화 버전이나 오버로딩 버전으로 선언하지만 않으면 됩니다.

 

정리를 해보겠습니다.

첫째, 표준에서 제공하는 swap이 여러분의 클래스 및 클래스 템플릿에 대해 납득할 만한 효율을 보이면, 그냥 아무것도 하지 마세요

둘째, 표준 swap의 효율이 기대한 만큼 충분하지 않다면, 다음과 같이 하십시오.

1. 여러분의 타입으로 만들어진 두 객체의 값을 바꾸는 함수를 swap라는 이름으로 만들고, 이것을 public 멤버 함수로 두십시오. 이 함수는 절대로 예외를 던져선 안 됩니다.

2. 클래스 혹은 템플릿이 들어 있는 네임스페이스와 같은 네임스페이스에 비멤버 swap을 만들어 넣습니다. 그리고 1번에서 만든 swap 멤버 함수를 이 비멤버 함수가 호출하도록 만듭니다.

3. 새로운 클래스를 만들고 있다면, 그 클래스에 대한 std::swap의 특수화 버전을 준비해 둡니다. 그리고 이 특수화 버전에서도 swap 멤버 함수를 호출하도록 만듭니다.

셋째, 사용자 입장에서 swap을 호출할 때, swap을 호출하는 함수가 std::swap을 볼 수 있도록 using 선언을 반드시 포함시킵니다. 그 다음에 swap을 호출하되, 네임스페이스 한정자를 붙이지 않도록 하십시오.

 

꼭 잊지 말아야 할 것!

1. std::swap이 여러분의 타입에 대해 느리게 동작할 여지가 있다면 swap 멤버 함수를 제공합시다. 이 멤버 swap은 예외를 던지지 않도록 만듭시다.

2. 멤버 swap을 제공했으면, 이 멤버를 호출하는 비멤버 swap도 제공합니다. 클래스(템플릿이 아닌)에 대해서는, std::swap도 특수화해 둡시다.

3. 사용자 입장에서 swap을 호출할 때는, std::swap에 대한 using 선언을 넣어 준 후에 네임스페이스 한정 없이 swap을 호출합시다.

4. 사용자 정의 타입에 대한 std 템플릿을 완전 특수화하는 것은 가능합니다. 그러나 std에 어떤 것이라도 새로 '추가'하려고 들지는 마십시오.

반응형

'언어 > C++' 카테고리의 다른 글

[Effective C++] 캐스팅은 절약, 또 절약! 잊지 말자  (0) 2020.01.27
[Effective C++] 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자  (0) 2020.01.26
[Effective C++] 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자  (0) 2020.01.24
[Effective C++] 멤버 함수보다는 비멤버 비프렌드 함수와 더 까가워지자  (0) 2020.01.23
[Effective C++] 데이터 멤버가 선언될 곳은 private 영역임을 명심하자  (0) 2020.01.22
'언어/C++' 카테고리의 다른 글
  • [Effective C++] 캐스팅은 절약, 또 절약! 잊지 말자
  • [Effective C++] 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자
  • [Effective C++] 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자
  • [Effective C++] 멤버 함수보다는 비멤버 비프렌드 함수와 더 까가워지자
지나가던 개발자
지나가던 개발자
지나가던 개발자
나의 발전을 위한 공간
지나가던 개발자
전체
오늘
어제
  • 분류 전체보기 (221)
    • 언어 (86)
      • C++ (43)
      • JAVA (43)
    • 게임 개발 (4)
      • 간단한 RPG 게임 만들기 (4)
      • 게임 개발 이슈 해결 (0)
    • 백준 문제풀이 (36)
      • PLATINUM (0)
      • GOLD (13)
      • SILVER (21)
      • BRONZE (2)
    • 프로그래머스 문제풀이 (32)
      • LEVEL 5 (0)
      • LEVEL 4 (0)
      • LEVEL 3 (7)
      • LEVEL 2 (19)
      • LEVEL 1 (6)
    • SQL 문제풀이 (15)
      • 프로그래머스 (4)
      • 해커랭크 (11)
    • 디자인 패턴 (1)
    • 웹 (17)
      • 웹 이론 정리 (17)
    • CS 지식 (28)
      • 알고리즘 (0)
      • 데이터베이스 (11)
      • 자료구조 (0)
      • 네트워크 (7)
      • 그래픽스 (0)
      • 운영체제 (9)
      • 기타 (1)
    • Git (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 깃 허브

공지사항

인기 글

태그

  • 객체 지향 설계
  • 클래스와 인터페이스
  • 설계 및 선언
  • BOJ
  • mysql
  • 프로그래머스
  • 해커랭크
  • 백준
  • c++
  • 열거 타입과 애너테이션
  • 상속
  • level 1
  • 소멸자 및 대입 연산자
  • Chapter 4
  • Gold 5
  • Silver 3
  • Chapter 6
  • Level 2
  • 객체 생성과 파괴
  • java

최근 댓글

최근 글

hELLO · Designed By 정상우.
지나가던 개발자
[Effective C++] 예외를 던지지 않는 swap에 대한 지원도 생각해 보자
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.