empty_bases
Microsoft 전용
C++ 표준에서는 가장 많이 파생된 개체의 크기가 0이 아니어야 하며 1바이트 이상의 스토리지를 차지해야 합니다. 요구 사항은 가장 파생된 개체로만 확장되므로 기본 클래스 하위 개체에는 이 제약 조건이 적용되지 않습니다. EBCO(빈 기본 클래스 최적화)는 이러한 자유를 활용합니다. 이로 인해 메모리 사용량이 감소하여 성능이 향상될 수 있습니다. Microsoft Visual C++ 컴파일러는 지금까지 EBCO에 대한 지원이 제한되었습니다. Visual Studio 2015 업데이트 3 이상 버전에서는 이 최적화를 최대한 활용하는 클래스 형식에 대한 새 __declspec(empty_bases)
특성을 추가했습니다.
Important
__declspec(empty_bases)
이 기능을 사용하면 ABI가 적용되는 구조 및 클래스 레이아웃이 변경될 수 있습니다. 이 스토리지 클래스 특성을 사용할 때 모든 클라이언트 코드가 코드와 구조 및 클래스에 대해 동일한 정의를 사용하는지 확인합니다.
구문
__declspec( empty_bases )
설명
Visual Studio에서 사양 __declspec(align())
이나 alignas()
사양이 없는 빈 클래스의 크기는 1 바이트입니다.
struct Empty1 {};
static_assert(sizeof(Empty1) == 1, "Empty1 should be 1 byte");
단일 비정적 데이터 멤버가 형식 char
인 클래스의 크기도 1 바이트입니다.
struct Struct1
{
char c;
};
static_assert(sizeof(Struct1) == 1, "Struct1 should be 1 byte");
클래스 계층 구조에서 이러한 클래스를 결합하면 크기가 1바이트인 클래스도 생성됩니다.
struct Derived1 : Empty1
{
char c;
};
static_assert(sizeof(Derived1) == 1, "Derived1 should be 1 byte");
이 결과는 2바이트 크기(1바이트 및 1 Empty1
Derived1::c
바이트)가 없으면 Derived1
작업 중인 빈 기본 클래스 최적화입니다. 빈 클래스 체인이 있는 경우에도 클래스 레이아웃이 최적입니다.
struct Empty2 : Empty1 {};
struct Derived2 : Empty2
{
char c;
};
static_assert(sizeof(Derived2) == 1, "Derived2 should be 1 byte");
그러나 Visual Studio의 기본 클래스 레이아웃은 여러 상속 시나리오에서 EBCO를 활용하지 않습니다.
struct Empty3 {};
struct Derived3 : Empty2, Empty3
{
char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // Error
크기는 1바이트일 수 있지만 Derived3
기본 클래스 레이아웃의 크기는 2바이트입니다. 클래스 레이아웃 알고리즘은 두 개의 연속 빈 기본 클래스 사이에 1바이트 안쪽 여백을 추가하여 결과적으로 Empty2
다음 내에서 Derived3
추가 바이트를 사용합니다.
class Derived3 size(2):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
1 | +--- (base class Empty3)
| +---
1 | c
+---
이 최적이외 레이아웃의 효과는 이후 기본 클래스 또는 멤버 하위 개체의 맞춤 요구 사항이 추가 안쪽 여백을 강제로 적용할 때 복합됩니다.
struct Derived4 : Empty2, Empty3
{
int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // Error
형식 int
의 개체에 대한 자연 맞춤은 4바이트이므로 다음을 올바르게 정렬Derived4::i
하려면 3바이트의 추가 안쪽 여백을 추가 Empty3
해야 합니다.
class Derived4 size(8):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
1 | +--- (base class Empty3)
| +---
| <alignment member> (size=3)
4 | i
+---
기본 클래스 레이아웃의 또 다른 문제는 빈 기본 클래스가 클래스의 끝을 지나 오프셋에 배치될 수 있다는 것입니다.
struct Struct2 : Struct1, Empty1
{
};
static_assert(sizeof(Struct2) == 1, "Struct2 should be 1 byte");
class Struct2 size(1):
+---
0 | +--- (base class Struct1)
0 | | c
| +---
1 | +--- (base class Empty1)
| +---
+---
Struct2
최적 크기 Empty1
이지만 오프셋 1 내에 Struct2
배치되지만 Struct2
크기를 고려하여 증가하지는 않습니다. 따라서 개체 배열 A
의 Struct2
경우 하위 개체 A[0]
의 Empty1
주소는 대/소문자를 구분하지 않아야 하는 주소A[1]
와 동일합니다. 이 문제는 내 오프셋 0Struct2
에 배치되어 하위 개체와 겹치는 Struct1
경우 Empty1
발생하지 않습니다.
이러한 제한 사항을 해결하고 EBCO를 완전히 활용하도록 기본 레이아웃 알고리즘이 수정되지 않았습니다. 이러한 변경으로 이진 호환성이 손상됩니다. EBCO의 결과로 클래스의 기본 레이아웃이 변경된 경우 클래스 정의를 포함하는 모든 개체 파일 및 라이브러리를 다시 컴파일해야 클래스 레이아웃에 모두 동의할 수 있습니다. 또한 이 요구 사항은 외부 원본에서 가져온 라이브러리로 확장됩니다. 이러한 라이브러리의 개발자는 다른 버전의 컴파일러를 사용하는 고객을 지원하기 위해 EBCO 레이아웃과 함께 컴파일된 독립 버전을 제공해야 합니다. 기본 레이아웃을 변경할 수는 없지만 클래스 특성을 추가하여 __declspec(empty_bases)
클래스별로 레이아웃을 변경하는 방법을 제공할 수 있습니다. 이 특성으로 정의된 클래스는 EBCO를 최대한 활용할 수 있습니다.
struct __declspec(empty_bases) Derived3 : Empty2, Empty3
{
char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // No Error
class Derived3 size(1):
+---
0 | +--- (base class Empty2)
0 | | +--- (base class Empty1)
| | +---
| +---
0 | +--- (base class Empty3)
| +---
0 | c
+---
모든 하위 개체 Derived3
는 오프셋 0에 배치되고 크기는 최적 1바이트입니다. 기억 __declspec(empty_bases)
해야 할 한 가지 중요한 점은 적용된 클래스의 레이아웃에만 영향을 줍니다. 기본 클래스에는 재귀적으로 적용되지 않습니다.
struct __declspec(empty_bases) Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // Error
class Derived5 size(8):
+---
0 | +--- (base class Derived4)
0 | | +--- (base class Empty2)
0 | | | +--- (base class Empty1)
| | | +---
| | +---
1 | | +--- (base class Empty3)
| | +---
| | <alignment member> (size=3)
4 | | i
| +---
+---
적용 __declspec(empty_bases)
되었지만 Derived5
직접 빈 기본 클래스가 없으므로 EBCO에 적합하지 않으므로 아무런 영향도 주지 않습니다. 그러나 대신 EBCO에 적합한 기본 클래스에 Derived4
적용되는 경우 둘 다 Derived4
Derived5
최적의 레이아웃을 갖습니다.
struct __declspec(empty_bases) Derived4 : Empty2, Empty3
{
int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // No Error
struct Derived5 : Derived4
{
};
static_assert(sizeof(Derived5) == 4, "Derived5 should be 4 bytes"); // No Error
class Derived5 size(4):
+---
0 | +--- (base class Derived4)
0 | | +--- (base class Empty2)
0 | | | +--- (base class Empty1)
| | | +---
| | +---
0 | | +--- (base class Empty3)
| | +---
0 | | i
| +---
+---
모든 개체 파일 및 라이브러리가 클래스 레이아웃 __declspec(empty_bases)
에 동의해야 하기 때문에 제어하는 클래스에만 적용할 수 있습니다. 표준 라이브러리의 클래스 또는 EBCO 레이아웃으로 다시 컴파일되지 않은 라이브러리에 포함된 클래스에는 적용할 수 없습니다.
Microsoft 전용 종료