EBO (Empty Base Optimization)
EBO는 상속관계에서 이야기되며, 빈 클래스를 상속할 때 발생하는 메모리 최적화를 말한다. (EBCO 라고도 불린다)
이 때, "빈 클래스" 라는 것은 가상 함수와 멤버 변수가 없는 클래스를 의미한다. (C++ 클래스 메모리 구조 참고)
class EmptyClass { void interface() { ... } };
int main() {
EmptyClass objects[10];
cout << sizeof(EmptyClass) << endl;
cout << sizeof(objects) << endl;
return 0;
}
평상시에는 생성된 객체들에 대한 식별을 보장하기 위해 빈 클래스의 크기를 0이 아닌 값으로 할당한다.
위의 코드를 예시로 들어보면, 만약 EmptyClass의 크기가 0이라면 배열을 통해 각 객체들을 식별할 수 없을 것이다.
따라서, 객체 고유성 유지를 위해 빈 클래스의 크기를 0이 아닌 값으로 할당받게 된다. 보통 1바이트를 할당한다.
실제로 위 코드를 출력해보면 1과 10이 출력된다.
이 때, 어떤 클래스가 EmptyClass를 상속하면 EmptyClass의 크기를 0바이트로 최적화하여 상속한다.
class EmptyClass { void interface() { ... } };
class User: private EmptyClass {
private:
int data;
};
int main() {
cout << sizeof(EmptyClass) << endl;
cout << sizeof(User) << endl;
return 0;
}
위의 예제를 예시로 들면 실제로 EmptyClass 클래스 자체의 크기는 1바이트 이지만,
상속을 받는 User 클래스의 크기를 출력해보면 int형 하나의 크기인 4바이트가 출력된다.
그러나 이 방식을 사용할 때 주의해야 할 점이 세 가지가 있다.
1. 파생 클래스에 기반 클래스의 인터페이스가 노출될 수 있다. (private 상속이면 사용은 못 하지만 멤버는 보인다)
2. EBO를 다중 상속을 사용하여 구현하면 컴파일러에 따라 기반 클래스들에 대한 최적화 방식이 다르다.
3. 파생 클래스에서의 기반 클래스 주소 결정은 컴파일러에 따라 다르게 동작한다. (같을 수도, 다를 수도 있다)
만약 1번 문제를 해결하고 싶으면 아래의 예시처럼 사용하면 해결할 수 있다.
class EmptyClass { void interface() { ... } };
template <typename T, typename Base>
class OptimizedBase: private Base {
private:
T iData;
};
class User {
private:
OptimizedBase<int, EmptyClass> data;
};
int main() {
cout << sizeof(User) << endl;
return 0;
}
이 코드에서는 기반 클래스의 인터페이스 노출 가능성을 없앴고, User 클래스에서의 접근 범위도 제한해줄 수 있다.
※ 만약 User에 EmptyClass 멤버 변수를 가지면 바이트 패딩에 의해 더 많은 메모리 영역을 사용할 수도 있다.
Reference