[c++] 중복 된 빈 기본 저장소가 vtable 포인터와 겹치지 않는 이유는 무엇입니까?

이 예제를 고려하십시오.

#include <iostream>

int main()
{
    struct A {};
    struct B : A {};
    struct C : A, B {};

    std::cout << sizeof(A) << '\n'; // 1
    std::cout << sizeof(B) << '\n'; // 1
    std::cout << sizeof(C) << '\n'; // 2, because of a duplicate base

    struct E : A {virtual ~E() {}};
    struct F : A, B {virtual ~F() {}};

    std::cout << sizeof(E) << '\n'; // 8, the base overlaps the vtable pointer
    std::cout << sizeof(F) << '\n'; // 16, but why?
}

(갓 볼트에서 실행)

여기 struct E에서 비어있는 기본 클래스 (1 바이트)는 예상대로 vtable 포인터와 동일한 스토리지를 사용합니다.

그러나 struct F빈베이스가 중복 된에 대해서는 이런 일이 발생하지 않습니다. 무엇이 원인입니까?

GCC, Clang 및 MSVC에서 동일한 결과를 얻습니다. 위의 결과는 x64에 대한 것 sizeof(void *) == 8입니다.


흥미롭게도 struct G : A, B {void *ptr;};GCC와 Clang의 경우 EBO를 수행하지만 (크기는 8) MSVC는 수행하지 않습니다 (크기는 16).



답변

컴파일러가 구조체 A 다음에 1 바이트 패딩을 추가하기 때문에

F {vptr (8) + 0 멤버 A + 1 패딩 (A가 비어 있기 때문에) +0 from b} = 9 컴파일러는 구조체의 스토리지를 정렬하기 위해 7 바이트 패딩을 추가합니다.

E {vptr (8) + A의 멤버 0 명 = 8 패딩 필요 없음

Microsoft에서

모든 데이터 객체에는 정렬 요구 사항이 있습니다. 구조의 경우 요구 사항이 가장 큰 멤버입니다. 오프셋 % alignment-requirement == 0이되도록 모든 객체에 오프셋이 할당됩니다.

https://docs.microsoft.com/en-us/cpp/c-language/storage-and-alignment-of-structures?view=vs-2019

편집하다:

여기 내 데모가 있습니다 :

int main()
{
    C c;
    A* a = &c;
    B* b = &c;

    std::cout << sizeof(A) << " " << a << '\n';
    std::cout << sizeof(B) << " " << b << '\n';
    std::cout << sizeof(C) << " " << &c << '\n';

    E e;
    a = &e;
    std::cout << sizeof(E) <<" " << &e << " " << a << '\n';

    F f;
    a = &f;
    b = &f;
    std::cout << sizeof(F) << " " << &f << " " << a << " " << b << '\n';

}

산출:

1 0000007A45B7FBB4
1 0000007A45B7FBB5
1 0000007A45B7FBB4
8 0000007A45B7FC18 0000007A45B7FC20
16 0000007A45B7FC38 0000007A45B7FC40 0000007A45B7FC41

보시다시피 a와 b는 서로 겹치지 않으며 여러 상속에서 vptr을 사용하면 각각 고유 한 포인터 값이 있습니다.

VC2019 x64 빌드로 컴파일 된 노트


답변