C++에서의 class와 struct의 차이는? 단 한가지?

2 Comments

누가 struct에서 class처럼 생성자나 연산자 재정의를 하는 것이나 member function을 정의할 수 있느냐라고 질문을 했다.

struct에서 생성자나 연산자 재정의는 자주 사용했지만, member function이라는 부분에서 (될 것 같았지만) 의구심이 들었다. 그 둘의 차이점은 무엇일까? 표준에서는…

결론은,

C++에서 struct와 class의 차이는 “단지” default access 모드의 차이이다.

(실제 테스트를 해보아도 모든 것을 다 할 수 있었다. 심지어 class에서 struct를 상속 받고 그 역도 가능한)

메모리 구조나 동작 방식도 거의 같은 것 같다.

[1] 아래 URL은 struct와 class의 차이를 질의 응답한 것이다.

http://blog.stevedoria.net/20050913/differences-between-cpp-classes-and-structs

http://carcino.gen.nz/tech/cpp/struct_vs_class.php

[2] 아래 링크를 가면 ISO/IEC JTC1/SC22/WG21 (C++ 표준 활동 그룹)의 2007년 C++ 표준의  draft 버전이 있는데 9장 Classes를 보면 class와 struct, union을 아예 하나로 묶어서 정의하고 있다.

※ union의 경우는 런 타임에 하나의 변수만 된다는 차이가 있을 뿐이다.

http://alones.kr/blog/843

물론, 이 사항은 컴파일러마다 컴파일러 버전 마다 틀려질 수 있을 것이다. 그것이 C++ 분석 도구를 만들거나 portability 고려 시 큰 걸림돌이 될 것이고.

[C++ STL] reverse_iterator에서 iterator로 변환을 위한 base 사용 시 주의 사항

6 Comments

STL 컨테이너를 역으로 순회하기 위해 reverse_iterator를 사용하고 있던 도중 다음과 같은 상황을 만나 iterator로 변경을 해야 하는 경우가 있다.

  1. 순 방향으로 순회하기 위해
  2. reverse_iterator를 인자로 받지 않는 컨테이너 함수나 알고리즘 함수를 만났을 경우
    일반적으로 컨테이너 요소를 삽입 (insert)하거나 삭제 (erase)하는 함수들은 reverse_iterator를 받지 않는다.

이 경우 reverse_iterator::base를 이용해서 현재 순회하고 있는 시점을 기준으로 iterator로 바꿀 수 있을 것입니다.

하지만 다음 코드는 대형 코드 내에서 많은 디버깅을 요할 수도 있고, 때론 재앙을 일으킬 수 있습니다.

(-_-;; 죽지 않고 단지 동작을 살짝 다르게 하면서 치명상을 입히니)

■ reverse_iterator::base()를 실수로 사용한 예제

STL 표준에 좀 더 충실한 (디버깅도 용이한) VS2005을 이용해서 예제 코드를 만들어봤습니다.

Vector에 1부터 5까지 넣고 reverse_iterator를 3에 가져다 둔 후에 base()를 이용해 그 위치를 가리키는 iterator를 만들고 3을 erase()로 지우려는 코드입니다.

(최소한 제가) 의도한 결과는 1,2,4,5 입니다.

하지만 결과는 1,2,3,5 입니다.

■ 원인

앞 예제의 원인은 reverse_iterator::base()의 구현 정책 때문에 발생하는 일입니다.

reverse_iterator

사용자 삽입 이미지위 그림과 같이 find()로 3에 해당하는 위치로 reverse_iterator로 옮긴 것은 예상하는 것과 같습니다.

하지만 reverse_iterator::base()로 it를 만든 것은 위 그림과 같이 4를 가리키는 결과가 됩니다.

왜일까요? 왜 base() 후 it가 같은 위치인 3을 가르키지 않을까요?

답 또한 위 그림에 있습니다.

일반적으로 iterator의 begin()과 end()의 정책은 begin()은 컨테이너의 첫 요소를 가리키게 하고 end()는 마지막 요소 다음의 아무 것도 없는 곳을 가리킵니다.

그래서 while( it != v.end()) 라는 것을 쓸 수 있겠죠?

같은 원리로 reverse_iterator의 rbegin()은 컨테이너의 마지막 요소를, rend()는 첫 요소의 앞을 가리키게 되어 있습니다.

base()는 reverse_iterator를 기준으로 iterator를 위치를 잡아주어야 하지만 위 그림의 어긋난 한 칸 (end()가 rebegin()보다 한 칸 오른쪽으로 가있습니다) 도 맞추어야 iterator가 문제없이 자기 고집대로 동작할 수 있겠죠?

그렇지 않다면 rend()가 begin()이 되어 begin()은 null을 가리키고 rbgein()은 end()가 되어 it != v.end() 를 이용한 loop들은 재앙을 맞이하겠죠?

아무튼 그래서 base()는 reverse_iterator가 가리키는 요소로부터 오른쪽 한 칸을 iterator의 위치로 지정해줍니다.

즉, 우리의 예제 코드에서 base()를 한 순간 it는 3이 아닌 한 칸 오른쪽에 있는 4를 가리키게 되었습니다.

MSDN의 VS2005 이상의 reverse_iterator::base()에서도 이 내용에 대해서 설명을 하고 있으며 아래와 같이 표현하고 있습니다.

&*(reverse_iterator ( i ) ) == &*( i – 1 ).

즉, iterator에 대한 reverse_iterator는 iterator의 왼쪽으로 한칸 이동 시킨 같과 같다는 뜻입니다. (저희 설명을 왼쪽으로 했구요)

■ 해결책

간단하게 ri를 왼쪽으로 한칸 더 밀어 넣고 base()를 사용하면 되겠죠?



여기서 주의할 것은 –ri.base()라고 작성하면 문제가 될 수있습니다.

(※ 제가 테스트 도중 초기 VC6.0 코드에서 컴파일이 되지 않는 것에만 집중해서 –ri.base()를 ++ri.base()로 잘못 쓴 것을 최익필님이 아래 댓글과 같이 지적 해주셔서 수정했습니다. 감사합니다. )

※ 이하 부분은 powell 님의 아래 지적과 그 것을 통해 재 확인 내용으로 수정했습니다. (2008.02.26)

즉, –ri.base() 코드는 컴파일러에 따라 컴파일될 수도 있고 되지 않을 수도 있다.

예로 VC6과 VC8 (VS2005)을 살펴보면,


먼저 VC8.0의 경우
는 reverse_iterator::base()는 아래와 같고 current는 _RanIt로 결국 vector의 wrapper iterator가 됩니다.

_RanIt __CLR_OR_THIS_CALL base() const
{ // return wrapped iterator
return (current);
}

그리고 vector의 해당 iterator인 _Vector_iterator인 operator–()을 가지고 있어 –ri.base()는 컴파일 가능합니다.


VC6.0
은 디버깅으로 따라가기 힘들어서 밝히진 못했지만, ri.base()로 반환된 형이 operator–()가 없기 때문에 컴파일 실패를 한 것 같습니다.

동일한 MS의 C++ library인데도 불구하고 버전에 따라 정책이 따르니 다른 컴파일러들은 더 다양하겠죠.

※ 이와 관련된 것은 아래 VC++ Team blog에 제가 문의한 것과 MSDN 포럼에 올려서 VC++ 팀의 Stephan T. Lavavej로부터 받은 답변을 보시면 도움이 되실 겁니다.

Ref: [1] MSDN reverse_iterator::base

       [2] Effective STL, Item 28

VS2005에서 reverse_iterator::base()의 C++ 표준 어김 예상

3 Comments

함수에서 반환하는 값을 바로 변경할 수 없는 것은 C++ 표준이다.

즉 다음과 같은 코드는 함수 호출이 r-value이기 때문에 컴파일 할 수 없다고 나온다 (VC6.0, VC8.0 – VS2005 동일)

즉, 아래와 같은 컴파일 에러 메시지를 만나게 된다. “++/–”는 변수와 같은 l-value에서 가능하기 때문이다.

error C2105: ‘++’에 l-value가 필요합니다.


[문제의 코드]

다음은 std::vector에 대해서 reverse_iterator를 이용해 순회하다가 특정 노드를 삭제하기 위해 reverse_iterator::base()를 이용해서 현재 참조하고 있는 vector의 위치를 iterator로 가르키게 하는 코드이다.

※ 참고
    1. reverse_iterator는 삽입 (insert)이나 삭제(erase)에서 사용할 수 없는 경우가 많습니다.
    2. reverse_iterator에서 reverse_iterator::base()로 iterator를 가르키게 하면, iterator는 현재 노드 보다 한 노드 뒤를 가르킵니다. 그래서 erase()를 위해서는 iterator를 한 노드 감소시켜야 합니다 (reverse_iterator에서 iterator로 변환을 위한 base 사용 시 주의 사항 참고)
(※ 아직 발행이 안되었습니다. 발행이 되면 이 참고 표를 지우겠습니다)

ri.base()로 반환되는 값을 — 연산자를 이용해서 변경시도하는 것은 MyMalloc() 예제와 같이 r-value를 변경 시도 한 것이기 때문에 컴파일 에러가 나야하는데 VC8.0에서는 허용해버리는 군요.

이러한 문제는 호환성에 문제가 있을 것입니다. VC8.0에서 –ri.base()와 같은 코드를 작성하고 이 것을 g++이나 VC6.0 등에 포팅할 때 문제가 생길 수 있겠죠.

VC6.0에 비해 VC8.0에서는 C++ 표준도 잘 따르고 (특히 STL) 디버깅 환경도 개선되어 좋아했는데 조금 아쉽습니다.

[Ref]

[1] MSDN Forms의 Visual C++ Language에 올려본 글입니다.
      reverse_iterator::base()’s
viloation of the C++ standard in VS2005

최신 C++ 표준 Draft version (2007.10.22)과 몇 가지 References

No Comments

JTC1/SC22/WG21 – The C++ Standard Committee 에 있는 것들 중심으로 표준과 몇 가지 Technical Report들의 References를 정리해봤습니다.

워킹 그룹의 사이트를 이리 저리 돌아다니면 읽을 것들이 너무 많은 것 같습니다. 아무튼 ~~

ISO/ IEC

[1] C++ Standard Draft Version

ISO/IEC JTC1/SC22/WG21 (C++ 표준 활동 그룹)의 최신 Draft version을 공유합니다.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf
- 예전엔 C++ 표준은 19달러인가를 내고 받았던 것 같은데 아무튼
- STL 내용도 내부에 있으니 표준을 지키지 않고 가끔 이상하게 구현하는 MS를 욕할 때 더 잘 쓸 수 있을 것 같습니다.

[2] TR1

[2-1]최근 TR1 (Technical Report1) 발표 등 C++도 많은 발전을 하고 있는 것 같습니다.
(regular expression, smart pointers, hash tables, random number generator 등의 features 제안됨)
(http://en.wikipedia.org/wiki/Technical_Report_1)

[2-2] TR1 (Draft Technical Report on C++ Library Extension)

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1836.pdf

[2-3] 아래 페이지에서 MS의 TR1에 대한 활동들도 엿볼 수 있습니다.
http://blogs.msdn.com/vcblog/archive/2008/01/08/q-a-on-our-tr1-implementation.aspx


[3] Technical Report on C++ Performance (2006.02.15)

http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

STL (Standard Template Library)에 대한 MSDN Reference

No Comments

STL은 C++에게 Java와 같은 편리성을 제공해준다 (수많은 container와 algorithm).

하지만 STL의 철학을 알고 제대로 사용하기는 쉽지 않은 것 같다.

특히, STL 표준은 있지만 Vendor마다 조금씩 틀리게 구현하는 경우가 많아서 더 많은 주의를 요한다.

또한 VC6.0과 같은 경우는 STL을 표준에 맞지 않게 구현한 부분도 있었다. 물론 이에 대해서 예전에 소개한 것처럼 VS2005에서는 많은 부분이 개선되었고 디버깅 또한 환상적으로 편해졌다.

(이제 대한 부분은 이 글을 참고)

아무튼 그래서 나는 STL을 쓸 때 가능한 표준을 참고하려고 하고 여의치 않을 경우는 Visual Studio에 있는 STL의 header file을 참고하기도 한다.

(실제 VC6.0에서의 STL에 대한 MSDN은 그것으로 무엇을 조심하기란 정말 어려웠던 것 같다)

하지만 VS2005에서 STL 구현이 표준을 따랐을 뿐만 아니라 MSDN도 풍성해졌다.

그래서 이에 대한 Web MSDN Library를 소개한다.

 사용자 삽입 이미지

VS2005 STL 관련 MSDN

카테고리를 보면 VS2008도 쉽게 찾을 수 있을 것이고 STL MSDN 카테고리 주위로 좋은 카테고리들이 많다.

MSDN / MSDN Library / Development Tools and Languages / Visual Studio 2005 / Visual Studio / Visual C++ / Reference / Libraries Reference / Standard C++ Library / Standard C++ Library Header Files

그리고 못났지만 VS6.0의 MSDN은 여기로 생각할 수 있다.

[참고]

사용자 삽입 이미지개인적으로는 cplusplus.com을 STL 관련 Reference로 많이 활용하고 있다. 이 블로그에서 많이 참고하고 있고…

Older Entries