스토리텔링 개발자

[Effective STL] 3. 컨테이너 요소 복사 본문

개발/Effective STL

[Effective STL] 3. 컨테이너 요소 복사

김디트 2024. 10. 31. 11:09
728x90

항목 3. 복사(Copy)는 컨테이너 안의 객체에 맞게 비용을 최소화하며, 동작은 정확하게 하자

 

 

 

컨테이너의 요소
  • 기본적으로 STL 컨테이너는 삽입, 삭제 시 복사 처리를 한다.
    • 컨테이너가 가진 객체는 넣을 당시의 것과 같은 것이 아니다.
    • 컨테이너에서 객체를 가져와도, 컨테이너에 있었던 것이 아니다.
  • 컨테이너 내부의 객체조차도 늘 같은 것이라 보장할 수 없다.
    • vector 등에 insert를 통해 데이터를 삽입하거나, erase로 지우거나 하는 경우.
      • 컨테이너의 객체들은 복사를 통해 밀려나고 밀려온다.(항목 5, 항목 14 참조)
    • 정렬 알고리즘이나 그와 비슷한 매커니즘의 함수를 호출하는 경우.
      • next_permutation, previous_permutation, remove, unique / rotate, reverse 등.(항목 32 참조)

 

 

 

컨테이너 요소 복사
  • 요소 객체의 복사 기능을 사용한다.
    • 즉, 복사 생성자, 복사 대입 연산자를 사용한다.
class Widget
{
public:
    Widget(const Widget&); // 복사 생성자
    Widget& operator=(const Widget&); // 복사 대입 연산자
    ...
};
  • 자동으로 만들어지는 복사 생성자, 대입 연산자는 얕은 복사를 한다.(Effective C++ 항목 11, 항목 27)

 

 

 

STL 복사로 인한 문제들
  • 복사에 드는 비용이 큰 객체를 컨테이너에 넣는다면
    • 수행 성능의 병목현상을 일으킬 수 있다.
  • 커스텀 객체의 복사 동작이 단순히 복사 뿐 아니라 다양한 기능을 포함한다면
    • 사이드 이펙트 문제도 있을 수 있다.(항목 8 참조)
  • 상속된 객체를 컨테이너에 넣으려 한다면
    • 삽입 중 객체 복사를 하다가 슬라이스 문제(object slicing)가 발생한다.(항목 22, 38 참조)
    • class SpecialWidget : public Widget { ... };
      
      vector<Widget> vw; // 부모 클래스의 객체를 요소로 사용한다.
      SpecialWidget sw;
      vw.push_back(sw); // 자식 클래스 인스턴스를 카피하며 슬라이싱이 발생한다.

 

 

 

복사를 고려한 STL 사용법
  • 객체에 대한 컨테이너가 아니라 포인터에 대한 컨테이너를 사용하도록 하자.
  • 포인터의 장점
    • 속도가 빠르다.
    • 복사 시 정확히 동작한다.
    • 슬라이스 문제가 발생하지 않는다.
  • 포인터의 단점
    • 메모리 해제가 필요하다.
    • 물론 이는 스마트 포인터로 해결 가능하다.(항목 7, 33 참조)

 

 

 

STL은 불필요한 복사 생성을 피하도록 설계되어 있다.
  • 기본 배열과 vector의 비교
Widget w[maxNumWidgets]; // 기본 배열
// maxNumWidgets 개의 배열 공간을 미리 할당해둔다.
// 즉, 선언과 동시에 기본 Widget 객체들이 maxNumWidgets개 생성된다.

vector<Widget> vw; // vector
// 지금 당장은 Widget 객체를 하나도 가지지 않는다.
// 필요할 때 메모리 공간이 자라난다.
// 필요하다면 공간을 예약할 수 있지만..
vw.reserve(maxNumWidgets); // 항목 14 참조
// 생성자가 불리진 않는다.
  • 즉, 복사를 많이 하긴 하지만, 기본 배열보다는 훨씬 효율적이다.
728x90
Comments