정보공간_1

[2기 대구 김길종] The Empty Base Class Optimization (EBCO) 본문

IT 놀이터/Elite Member Tech & Talk

[2기 대구 김길종] The Empty Base Class Optimization (EBCO)

알 수 없는 사용자 2012. 8. 18. 23:12

The Empty Base Class Optimization (EBCO)

 

오늘은 C++ class 관한 내용 흥미로운 부분이 있어서 소개해 보고자 합니다.

간단한 내용이니 편하게 보시면 될 것 같습니다

.

 

1) Empty Class

Empty란 텅 빈 것을 말하는데요 C++에서의 empty classclass의 내부 구현 상으로 memory를 요구하지 않는 것을 말합니다. 아무것도 없는 텅 빈 클래스는 물론이고 일반적으로 virtual function(가상함수) static이 아닌 변수 등이 포함되지 않은 class를 가리킵니다. (가상 함수가 들어가게 되면 VC++에서는 가상 함수 테이블을 가리키는 포인터가 추가되어 4바이트가 늘어납니다)

 

2) sizeof(Empty Class)

그러면 이런 empty class의 크기를 구해보면 어떤 값이 나올까요? 간단하게 아무것도 없는 클래스를 작성하고 그 크기를 재보겠습니다

아무것도 없으니 당연히 0이 나올 것 같습니다. 하지만 실제로 실행했을 때 그 결과는 size: 1 과 같이 0이 아닌 1 byte가 나옵니다. (정렬에 좀 더 엄격한 시스템에서는 다른 숫자가 나오기도 한다고 합니다) 다른 예를 들어보겠습니다

 

위의 결과는 어떻게 될까요? 예상하셨겠지만 세 클래스 다 1 (byte)이 나오는걸 보실 수 있습니다.

size of A: 1 

size of B: 1

size of C: 1

 

3) why?

조금 어리둥절 할 수도 있습니다. 0이 아니고 1 byte일까? 빠른 이해를 위해 size 0 class가 있다고 가정하고 배열을 선언해 보겠습니다.

ZeroClass z[10]; // 만약 class의 크기가 0이라면 배열의 크기도 0이 될 것입니다 ( 0 x 10 )

여기까지만 봐도 뭔가 이상하다는 것을 알 수 있습니다. 조금 더 나아가 포인터 연산을 해보면

ZeroClass* pz = z;

z++;

위에서 z를 증가시키는 부분이 있는데 포인터의 주소는 증감 시키면 해당 타입의 크기가 곱해져서 계산이 됩니다. 즉 위의 식은 z = z + (1*sizeof(z)); 이 되는데 sizeof(z) 0이니까 결과적으로는 z = z가 되겠죠. 이것은 첨자 연산에도 적용이 되어 z[1] … z[10] *(z + 1) … *(z + 10) 도 결국 모두 z와 같은 값을 가지게 됩니다. z + i 연산을 했을 때 i sizeof(z)가 곱해질 테니까요. 첨자 연산은 물론이고 주소와 관련된 산술연산이 아예 소용 없게 되어 버립니다.

좀 더 간단하게 생각해서 메모리 공간에 여러 class instance들이 있다고 가정해보겠습니다. 만약 크기가 0 instance가 존재한다면 차지하는 공간이 없으므로 주소로 구분이 불가능하게 됩니다. 결국 1 byte를 할당해 준 것은 메모리 공간에 자리를 차지하게 함으로써 instance를 구분하기 위한 것입니다.

 

4) empty class가 다른 클래스의 base class가 될 때

여기쯤 오면 궁금증이 생길 수 있습니다. 구분을 위해 1 byte를 할당한다고 했는데 그럼 해당 class (empty class)를 다른 class가 상속 받는다면 어떻게 될까요? Base class 1이 더해질까요? 간단한 예제를 작성해 보겠습니다.

제목을 보고 예상하신 분도 계시겠지만 위의 코드를 VC++에서 돌려보면 모두 1로 같은 값이 나오게 됩니다. VC++ 컴파일러엔 여기에서 설명하려는 EBCO (empty base class optimization)가 구현되어있기 때문입니다. EmptyDerivedOne class EmptyClass를 상속하면 EmptyDerivedOne 에서 EmptyClass가 차지하는 공간이 없도록 optimization 되어 크기가 1이 되며 이렇게 최적화된 EmptyDerivedOne class를 상속받은 EmptyDerivedTwo class 의 크기 역시 1이 되게 됩니다. 만약 컴파일러에 EBCO가 구현되어있지 않다면 셋 모두 다른 크기를 지니게 될 것입니다.

<EBCO가 구현된 컴파일러의 경우>

<EBCO가 구현되지 않은 컴파일러의 경우>

 

이것은 클래스가 간접적으로 중첩되어 있을 경우도 마찬가지로 작용합니다.

 위의 결과는 예상하셨듯이 모두 1이 나옵니다. 참고했던 사이트에는 마지막의 NonEmpty class의 사이즈가 1이 아니라고 나와 있는데요 (두 개의 다른 object가 같은 offset을 가지는 것은 허락되지 않는다). VC++의 컴파일러에서는 간접적으로 중첩된 class까지 EBCO가 적용이 되어 1이 나오게 됩니다.

 

5) Empty class를 멤버로 가졌을 때

Empty class를 상속받지 않고 멤버 필드로 선언했습니다. 이것은 맨 앞의 Empty class 정의와 비교했을 때 일단 변수가 존재하므로 더 이상 Empty class가 아니게 되며 1 byte를 따로 할당 받지 않습니다. NonEmpty 안의 Empty 클래스의 객체가 개당 1 byte (Empty class이므로 1byte가 할당됨) 를 차지하게 되므로 결과는 차례대로 1 1 2가 나오게 될 것입니다.

Sizeof(Empty): 1

Sizeof(NonEmpty): 1

Sizeof(NonEmpty): 2

 

Reference

http://www.stroustrup.com/ - Bjarne Stroustrup's Homepage

http://alones.kr/blog/749 - Alones' blog

http://www.informit.com/articles/article.aspx?p=31473&seqNum=2 - Article

http://www.velocityreviews.com/forums/t268090-re-why-empty-class-size-is-1-byte.html - forum