Bagikan melalui


empty_bases

Khusus Microsoft

Standar C++ mengharuskan objek yang paling turunan harus memiliki ukuran bukan nol dan harus menempati satu atau beberapa byte penyimpanan. Karena persyaratan hanya meluas ke objek yang paling turunan, subobjek kelas dasar tidak tunduk pada batasan ini. Pengoptimalan Kelas Dasar Kosong (EBCO) memanfaatkan kebebasan ini. Hal ini mengakibatkan berkurangnya konsumsi memori, yang dapat meningkatkan performa. Kompilator Microsoft Visual C++ secara historis memiliki dukungan terbatas untuk EBCO. Di Visual Studio 2015 Update 3 dan versi yang lebih baru, kami telah menambahkan atribut baru __declspec(empty_bases) untuk jenis kelas yang memanfaatkan sepenuhnya pengoptimalan ini.

Penting

Penggunaan __declspec(empty_bases) dapat menyebabkan perubahan yang melanggar ABI dalam struktur dan tata letak kelas tempatnya diterapkan. Pastikan bahwa semua kode klien menggunakan definisi yang sama untuk struktur dan kelas sebagai kode Anda saat Anda menggunakan atribut kelas penyimpanan ini.

Sintaks

__declspec( empty_bases )

Keterangan

Di Visual Studio, tidak ada spesifikasi atau alignas() apa pun__declspec(align()), kelas kosong berukuran 1 byte:

struct Empty1 {};
static_assert(sizeof(Empty1) == 1, "Empty1 should be 1 byte");

Kelas dengan satu anggota data non-statis dari jenis char juga berukuran 1 byte:

struct Struct1
{
  char c;
};
static_assert(sizeof(Struct1) == 1, "Struct1 should be 1 byte");

Menggabungkan kelas-kelas ini dalam hierarki kelas juga menghasilkan kelas berukuran 1 byte:

struct Derived1 : Empty1
{
  char c;
};
static_assert(sizeof(Derived1) == 1, "Derived1 should be 1 byte");

Hasil ini adalah Pengoptimalan Kelas Dasar Kosong di tempat kerja, karena tanpa ukurannya Derived1 adalah 2 byte: 1 byte untuk Empty1 dan 1 byte untuk Derived1::c. Tata letak kelas juga optimal ketika ada rantai kelas kosong:

struct Empty2 : Empty1 {};
struct Derived2 : Empty2
{
  char c;
};
static_assert(sizeof(Derived2) == 1, "Derived2 should be 1 byte");

Namun, tata letak kelas default di Visual Studio tidak memanfaatkan EBCO dalam beberapa skenario pewarisan:

struct Empty3 {};
struct Derived3 : Empty2, Empty3
{
  char c;
};
static_assert(sizeof(Derived3) == 1, "Derived3 should be 1 byte"); // Error

Meskipun Derived3 ukurannya bisa 1 byte, tata letak kelas default menghasilkan ukuran 2 byte. Algoritma tata letak kelas menambahkan 1 byte padding antara dua kelas dasar kosong berturut-turut, secara efektif mengakibatkan penggunaan byte tambahan dalam Empty2Derived3:

class Derived3  size(2):
   +---
0  | +--- (base class Empty2)
0  | | +--- (base class Empty1)
   | | +---
   | +---
1  | +--- (base class Empty3)
   | +---
1  | c
   +---

Efek dari tata letak suboptimal ini diperparah ketika persyaratan penyelarasan kelas dasar yang lebih baru atau padding ekstra paksa subobjek anggota:

struct Derived4 : Empty2, Empty3
{
  int i;
};
static_assert(sizeof(Derived4) == 4, "Derived4 should be 4 bytes"); // Error

Perataan alami untuk objek jenis int adalah 4 byte, sehingga 3 byte padding tambahan harus ditambahkan setelah Empty3 untuk meratakan Derived4::idengan benar :

class Derived4 size(8):
   +---
0  | +--- (base class Empty2)
0  | | +--- (base class Empty1)
   | | +---
   | +---
1  | +--- (base class Empty3)
   | +---
   | <alignment member> (size=3)
4  | i
   +---

Masalah lain dengan tata letak kelas default adalah bahwa kelas dasar kosong dapat ditata pada offset melewati akhir kelas:

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)
   | +---
   +---

Meskipun Struct2 ukuran optimal, Empty1 diletakkan pada offset 1 dalam Struct2 tetapi ukurannya Struct2 tidak ditingkatkan untuk memperhitungkannya. Akibatnya, untuk array AStruct2 objek, alamat Empty1 subobjek A[0] akan sama dengan alamat A[1], yang seharusnya tidak terjadi. Masalah ini tidak akan terjadi jika Empty1 diletakkan di offset 0 dalam Struct2, sehingga tumpang tindih Struct1 dengan subobjek.

Algoritma tata letak default belum dimodifikasi untuk mengatasi batasan ini dan sepenuhnya memanfaatkan EBCO. Perubahan seperti itu akan merusak kompatibilitas biner. Jika tata letak default untuk kelas berubah sebagai akibat dari EBCO, setiap file objek dan pustaka yang berisi definisi kelas perlu dikompresi ulang sehingga semuanya menyetujui tata letak kelas. Persyaratan ini juga akan meluas ke pustaka yang diperoleh dari sumber eksternal. Pengembang pustaka tersebut harus menyediakan versi independen yang dikompilasi baik dengan dan tanpa tata letak EBCO untuk mendukung pelanggan yang menggunakan versi kompilator yang berbeda. Meskipun kami tidak dapat mengubah tata letak default, kami dapat menyediakan sarana untuk mengubah tata letak berdasarkan per kelas dengan penambahan __declspec(empty_bases) atribut kelas. Kelas yang ditentukan dengan atribut ini dapat menggunakan EBCO secara penuh.

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
   +---

Semua subobjek Derived3 diletakkan pada offset 0, dan ukurannya adalah 1 byte yang optimal. Salah satu poin penting yang perlu diingat adalah bahwa __declspec(empty_bases) hanya memengaruhi tata letak kelas yang diterapkannya. Ini tidak diterapkan secara rekursif ke kelas dasar:

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
   | +---
   +---

Meskipun __declspec(empty_bases) diterapkan ke Derived5, itu tidak memenuhi syarat untuk EBCO karena tidak memiliki kelas dasar kosong langsung, sehingga tidak berpengaruh. Namun, jika sebaliknya diterapkan ke Derived4 kelas dasar, yang memenuhi syarat untuk EBCO, keduanya Derived4 dan Derived5 akan memiliki tata letak yang optimal:

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
   | +---
   +---

Karena persyaratan bahwa semua file objek dan pustaka menyetujui tata letak kelas, __declspec(empty_bases) hanya dapat diterapkan ke kelas yang Anda kontrol. Ini tidak dapat diterapkan ke kelas di pustaka standar, atau ke kelas yang disertakan dalam pustaka yang tidak juga dikompresi ulang dengan tata letak EBCO.

END Khusus Microsoft

Baca juga

__declspec
Kata kunci