스토리텔링 개발자

[Effective STL] 30. 알고리즘 사용 시 목적지 범위(destination range) 주의 본문

개발/Effective STL

[Effective STL] 30. 알고리즘 사용 시 목적지 범위(destination range) 주의

김디트 2024. 12. 17. 10:57
728x90

항목 30. 알고리즘의 데이터 기록 범위(destination range)는 충분히 크게 잡자

 

 

 

컨테이너 요소 삽입 시
int transmogrify(int x); // 정수 x를 받아 어떤 새로운 값을 만드는 함수

vector<int> values;
... // values에 데이터를 넣는다.

// values 요소들에 transmogrify를 적용하고 result에 삽입한다.
// 하지만, 버그가 있다.
vector<int> results;
transform(values.begin(), values.end(), results.end(), transmogrify);
  • transform 결과 기록 순서
    1. values[0]을 매개변수로 가져온다.
    2. transmogrify를 호출하여 값을 변환한다.
    3. *results.end()에 대입한다.
    4. values[1]에 대해 1번부터 다시 진행
  • 허나, 이 시점에서 results에는 공간이 할당되어 있지 않다.
    • 즉, 목적지 범위인 results.end()에는 아무런 객체가 없다.
  • 알고리즘이 알아서 컨테이너에 insert하며, 컨테이너가 공간을 할당할 것이라 기대했지만..
    • 그렇게 하려면 STL에게 똑바로 알려줘야 한다.
vector<int> results;
transform(values.begin(), values.end(), back_inserter(results), transmogrify);
// 이 경우, back_inserter는 push_back을 호출하게 되어 있으므로,
// 대상 컨테이너는 반드시 push_back을 지원해야 한다.

 

 

 

results 앞에다가 transform 결과를 추가하고 싶다면?
  • 물론 그냥 front_inserter를 사용할 수 있지만..
    • 객체들을 순서대로 각각 push_front 시키므로 순서가 반대로 삽입된다.
    • vector는 push_front를 지원하지 않는다.
  • values를 뒤집어서 넣어주면 된다.
list<int> results;
transform(values.rbegin(), values.rend(), front_inserter(results), transmogrify);

 

 

 

임의의 위치를 지정하여 결과를 추가하고 싶다면?
  • 반복자 어댑터를 사용하면 된다.(항목 5 참조)
vector<int> results;
... // 데이터가 몇 개 들어있다.

transform(values.begin(), values.end(),
          inserter(results, results.begin() + results.size() / 2), // 중간에 삽입
          transmogrify);

 

 

 

오버헤드 줄이기
  • 이 모든 inserter들은 한번에 한 개의 요소만 삽입하는데, 이는 오버헤드이다.(항목 5 참조)
    • 하지만, transform은 어쨌든 한 번에 하나의 값을 목적지 범위에 저장하기에 바꿀 수 있는 방법은 없다.
  • 대상 컨테이너가 vector, string이라면 reserve를 호출하여 오버헤드를 최소화할 수 있다.(항목 14 참조)
vector<int> values;
vector<int> results;
...
reuslts.reserve(results.size() + values.size()); // 공간 확보

transform(values.begin(), values.end(),
          inserter(results. results.begin() + results.size() / 2),
          transmogrify);
  • reserve는 컨테이너의 요소 크기가 아니라  용량(capacity)만을 증가시킨다는 사실을 기억할 것.
    • 그러므로 reserve를 호출한 후에도 반복자 어댑터를 사용해야 한다.
vector<int> values;
vector<int> results;
...
results.reserve(results.size() + values.size());
// transform(values.begin(), values.end(), results.end(), transmogrify);
// reserve를 했더라도 위 코드는 미정의 동작!!
// 아래처럼 해야 한다.
transform(values.begin(), values.end(), back_inserter(results), transmogrify);

 

 

 

새 요소가 아니라 원래의 요소를 덮어쓰고 싶은 경우?
  • 방법 1 : resize로 크기를 확보한다.
vector<int> values;
vector<int> results;
...
if(results.size() < values.size())
{
    // results의 크기를 values의 크기만큼으로 맞춘다.
    results.resize(values.size());
}

// values를 results에 덮어쓴다.
transform(values.begin(), values.end(), results.begin(), transfmogrify);
  • 방법 2 : results를 clear한 후 반복자 어댑터를 사용한다.
...
results.clear(); // 요소 모두 삭제

results.reserve(values.size());
transform(values.begin(), values.end(), back_inserter(results), transmogrify);

 

728x90
Comments