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 Empty1Derived1::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 크기를 고려하여 증가하지는 않습니다. 따라서 개체 배열 AStruct2 경우 하위 개체 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 적용되는 경우 둘 다 Derived4Derived5 최적의 레이아웃을 갖습니다.

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 전용 종료

참고 항목

__declspec
키워드