항목 26 : const_iterator나 reverse_iterator, const_reverse_iterator도 좋지만 역시 쓸만한 것은 iterator이다

iterator->const_iterator의 변환은 가능하지만 역은 성립이 되지 않기 때문에 요소의 삽입이나 삭제 위치를 지정하는데 있어서 상수 반복자는 대개 쓸모가 없다.

'대개' 쓸모가 없다는 것이지 아예 쓸모가 없는건 아니다. 알고리즘은 반복자의 종류에 상관없이 동작하기 때문이다.

다만 어떤 형태의 insert와 erase는 꼭 iterator만을 넘겨야만 한다.

 

typedef std::deque<int> IntDeque;
IntDeque::iterator i;
IntDeque::const_iterator ci;
...

if (i == ci)

위의 코드는 iterator를 const_iterator로 암시적인 변환을 해주지 않는다면 컴파일러에 따라 오류를 발생시킬 수도 있다.

 

웬만하면 반복자의 타입을 이것저것 사용하지 않고 iterator만 사용하는 것이 낫다.

 

 

항목 27 : const_iterator를 iterator로 바꾸는 데에는 distance와 advance를 사용하자

const_iterator에 const_cast를 한다고 상수성이 제거되지는 않는다. (vector, string은 예외)

하지만 생각보다 쉽게 안전하고 이식성 있게 변환시킬 수 있다.

const_iterator의 거리를 구해서 iterator를 그만큼 이동시키면 끝이다.

 

std::deque<int> d;
typedef std::deque<int>::iterator Iter;
std::deque<int>::const_iterator CIter; // d의 요소를 가리킨다
...

Iter i(d.begin();
CIter ci = d.find(3);

std::advance(i, std::distance(i, ci));
// std::advance(i, std::distance<CIter>(i, ci));
// const_iterator->iterator 변환이 안된다면 템플릿 인자로 명시한다

또 하나 언급하자면 vector같은 임의 접근 반복자는 상수시간, 양방향 반복자는 선형시간에 특정 반복자에 접근할 수 있기 때문에 정말 필요한게 아니라면 const_iterator를 iterator로 변환하지 않는것이 좋다.

 

 

항목 28 : reverse_iterator에 대응되는 기점 반복자를 사용하는 방법을 정확하게 이해하자

 

std::vector<int> v;

for(int i = 1; i <= 5; ++i) {
    v.push_back(i);
}
std::vector<int>::reverse_iterator ri = std::find(v.rbegin(), v.rend(), 3);
std::vector<int>::iterator i(ri.base());

 

코드를 실행하면 iterator와 reverse_iterator는 그림과 같이 배치된다. ri.base()는 순향 반복자 기준 다음 위치를 반환해준다.

 

◾ 만약 요소의 삽입이 이뤄진다면?

insert는 iterator가 가리키는 위치 바로 앞에서 이뤄진다. 그래서 base로 반환받은 순향 반복자에 별다른 연산을 할 필요없이 바로 삽입을 수행하면 된다.

 

◾ 만약 요소의 삭제가 이뤄진다면?

삽입과 다르게 가리키고 있는 요소를 삭제하기 때문에 대응되지 않는다. 그래서 base로 반환받은 반복자의 한칸 앞의 요소를 삭제해주어야 한다.

그런데 vector나 string의 경우, 반복자가 const T*로 되어있기 때문에 아예 역방향 반복자를 한칸 더 이동시키고 base를 반환받아서 사용해야한다.

 

v.erase((++ri).base());
// --ri.base() 가 아닌 이유 : 
// 벡터는 반복자가 const T*로 이뤄져있기 때문에 반환받은 반복자에 연산을 할 수 없다

 

 

항목 29 : 문자 단위의 입력에는 istreambuf_iterator의 사용도 적절하다

std::ifstream inputFile("interestingData.txt");
std::string fileData((std::istream_iterator<char>(inputFile)), std::istream_iterator<char>());

아쉽게도 위의 코드는 공백 문자를 복사하지 못한다. istream_iterator는 operator<<를 사용하고 공백 문자를 건너뛰기 때문이다.

 

inputFile.unsetf(std::ios::skipws);

입력 스트림의 skipws 플래그를 해제하면 공백 문자를 건너뛰지 않겠지만 생각보다 복사 시간이 빠르지 않다.

 

std::ifstream inputFile("interestingData.txt");

std::string fileData((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>());

대신 istreambuf_iterator를 사용하면 스트림 자체의 버퍼를 직접 건드려서 다음 문자들을 바로 읽어들인다.

skipws의 플래그도 해제할 필요가 없다.

'도서 > Effective STL' 카테고리의 다른 글

알고리즘(Algorithms) (2)  (0) 2022.12.20
알고리즘(Algorithms) (1)  (0) 2022.12.20
STL 연관 컨테이너 (2)  (0) 2022.12.20
STL 연관 컨테이너 (1)  (0) 2022.12.20
vector와 string  (0) 2022.12.18

+ Recent posts