더북(TheBook)

객체들이 이동 시맨틱을 갖는 타입(주로 임시 객체)의 우측값(rvalue)*이 아니면 모든 STL 컨테이너는 객체를 컨테이너에 저장할 때 복제본을 저장한다. STL에서는 이동 생성자와 이동 할당 연산자가 반드시 noexcept로 지정되어야 한다. noexcept로 지정하면 예외를 던지지 않는다. 이동 시맨틱이 없는 타입의 객체를 컨테이너에 추가하고 원본을 수정하면 원본과 컨테이너에 있는 객체는 서로 다른 값을 갖게 된다. 하지만 객체를 가져올 때 컨테이너에서 객체에 대한 참조를 가져오면 저장된 객체를 수정할 수 있다. 컨테이너에 저장된 객체의 복제본은 객체의 타입에서 제공하는 복제 생성자를 사용해서 생성된다. 이런 경우에 컨테이너에 객체에 대한 포인터를 저장하거나 타입에 대한 이동 시맨틱이 구현되어 있다면 객체를 컨테이너에 이동하는 방법이 더 좋다.

Caution

기반(base) 클래스 타입을 원소로 저장하는 컨테이너에 파생(derived) 클래스 객체를 저장하지 마라. 파생 클래스 객체를 컨테이너에 저장하면 복사 손실(slicing)이 발생하게 된다. 다형성 측면에서 컨테이너에 있는 파생 클래스 객체에 접근하고 싶다면 기본 클래스 포인터를 저장한 컨테이너에 파생 클래스 객체에 대한 포인터를 저장해야 한다. 또는 더 좋은 방법으로는 기본 타입에 대한 스마트 포인터를 저장해야 한다.

컨테이너는 객체들을 힙에 저장하며 객체가 차지하는 공간도 자동으로 관리한다. 타입 T의 객체들을 저장할 컨테이너에 공간을 할당하는 작업은 할당자(allocator)가 담당하며, 할당자의 타입은 템플릿 매개변수로 지정한다. 기본 타입 인수는 std::allocator<T>로 타입 T의 객체를 힙 메모리에 할당하는 할당자라는 뜻이다. 할당자 타입이 템플릿 매개변수로 되어 있으므로 필요하다면 직접 구현한 할당자를 사용하는 것도 가능하다. 성능상의 이유로 할당자를 직접 구현할 수도 있지만, 이런 경우는 드물고, 대부분은 기본 할당자로도 좋은 성능을 낸다. 할당자를 직접 정의하는 것은 고급 주제라서 이 책에서는 다루지 않을 것이다. 따라서 할당자를 쓸 때 마지막 템플릿 매개변수를 템플릿 타입에서 생략하겠다. std::vector<typename T, typename Allocator> 템플릿은 Allocatorstd::allocator<T>로 지정된 기본값을 갖는다. 여기서 이렇게 설명한 이유는 할당자도 직접 정의해서 쓸 수 있다고 알려주기 위해서다.

* 역주

우측값(rvalue)은 상수나 임시 객체를 말한다. 우측값은 우측값을 사용하는 식에서만 유지되는 임시 값을 말한다. 반면에 좌측값(lvalue)은 단일 식 이후에도 유지되는 값을 말한다. int a = 1 + 2;에서 a는 좌측값이고, 1 + 2는 우측값이다. 1 + 2는 해당 식에서만 유지되는 임시 값으로 계산되기 때문에 우측값이다. a는 a를 정의하는 식 이후에도 유지되기 때문에 좌측값이다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.