dev.log2004. 1. 20. 01:13
오늘 모 사이트의 질문/답변란에서 다음과 같은 코드를 보았다.

ofstream out( filename );
vector< ClientData > clientVector;
clientVector.push_back( ... );
....
out.write(
reinterpret_cast< const char* >( &clientVector[0] ),
clientVector.size() * sizeof( clientVector[0] ) );

이 얼마나 C++스럽지 않은 코드인가!

왜 C++스럽지 않은지를 살펴보면, 우선 세가지 문제가 눈에 걸린다.

1) C++의 타입시스템을 무시하고 있다.
C++에서 특히 강화된 것 중의 하나가 타입시스템이다. 따라서 C++을 C++스럽게 쓰려면 타입시스템의 범위 내에서 동작을 만들어주는 것이 보다 양질의 코드를 생산하기 위한 가이드가 된다. 그러나 위의 코드는 vector가 담고 있는 원소의 주소를 강제로 캐스팅하여 파일에 저장하는 작업을 하고 있다.

2) 범용성이 부족하다.
STL 은 전적으로 이터레이터를 기반으로 구축된 라이브러리이다. 모든 컨테이너와 알고리듬은 어떤 형태로든 이터레이터를 기반으로 동작하도록 인터페이스가 구성되어 있다. (물론, 이터레이터를 제공하지 않는 stack, queue등의 컨테이너는 제외) 따라서 C++다운 코드를 구성하고자 한다면 STL컨테이너와 입출력스트림에 대한 작업은 이터레이터를 통해 하는 것이 정석이다. 컨테이너가 벡터가 아닌 다른 컷으로 바뀌더라도 동일한 코드로 동작할 수 있도록 하기 위함이다. 그러나 위의 코드는 이터레이터가 아닌, 메모리 주소를 가지고 작업하는 C스타일의 코드를 고수하고 있다. 당연히 벡터 이외의 다른 컨테이너가 필요할 경우에는 동작하지 않는다.


그렇다면 어떻게 고쳐야 'C++스러운' 코드가 될까?
여 러가지 방법이 있을 수 있겠지만, 한가지 방법은 ostream_iterator를 사용하는 것이다. ostream_iterator를 사용하기 위해서는 << 연산자를 (insertion operator라고 부른다) 오버로딩한 후 파일스트림을 가지고 원하는 이터레이터를 생성하면 된다. 다음 코드를 보자.

ofstream& operator<< ( ostream& os, ClientData& data )
{
os.wirte(
reinterpret_cast< const char* >&data,
sizeof( data ) );
return os;
}


이제 출력하기 위해서 ostream_iterator를 생성하고 copy알고리즘을 생성한 이터레이터에 대해 적용하면 된다.
copy( clientVector.begin(), 
clientVector.end(),
ostream_iterator< ClientData >( out ) );


물론 여기서도 캐스팅이 쓰이긴 했지만 이는 vector객체에 대해 쓰여진 것을 좀더 하위 레벨의 ClientData 타입으로 낮췄다는 면에서는 훨씬 더 나은 코드가 된다.
이 코드가 더 나아진 점은, clientVector 객체가 굳이 vector 컨테이너일 필요가 없다는 것이다. 컨테이너의 종류에 신경쓰지 말고 원하는 대로 담은 다음에 그저 copy 알고리듬을 호출하기만 하면 모든 것은 컴파일러가 알아서 생성해 준다는 것이다.

Posted by uhm