Saya telah menemukan istilah ini tipe POD beberapa kali.
Apa artinya?
Saya telah menemukan istilah ini tipe POD beberapa kali.
Apa artinya?
Jawaban:
POD adalah singkatan dari Plain Old Data - yaitu, kelas (baik didefinisikan dengan kata kunci struct
atau kata kunci class
) tanpa fungsi konstruktor, destruktor dan anggota virtual. Artikel Wikipedia tentang POD masuk ke sedikit lebih detail dan mendefinisikannya sebagai:
Struktur Data Lama Biasa dalam C ++ adalah kelas agregat yang hanya berisi PODS sebagai anggota, tidak memiliki destruktor yang ditentukan pengguna, tidak ada operator penugasan salinan yang ditentukan pengguna, dan tidak ada anggota tipe pointer-ke-anggota yang tidak statis.
Detail lebih lanjut dapat ditemukan dalam jawaban ini untuk C ++ 98/03 . C ++ 11 mengubah aturan seputar POD, membuat mereka sangat rileks, sehingga memerlukan jawaban tindak lanjut di sini .
POD adalah tipe (termasuk kelas) di mana kompiler C ++ menjamin bahwa tidak akan ada "keajaiban" yang terjadi dalam struktur: misalnya pointer tersembunyi ke vtables, offset yang diterapkan ke alamat ketika dilemparkan ke tipe lain ( setidaknya jika POD target juga), konstruktor, atau destruktor. Secara kasar, suatu tipe adalah POD ketika satu-satunya yang ada di dalamnya adalah tipe dan kombinasi bawaannya. Hasilnya adalah sesuatu yang "bertindak seperti" tipe C.
int
, char
, wchar_t
, bool
, float
, double
Yang PODS, seperti long/short
dan signed/unsigned
versi mereka.enums
adalah PODconst
atau volatile
POD adalah POD.class
, struct
atau union
POD adalah POD dengan ketentuan bahwa semua anggota data non-statis adalah public
, dan tidak memiliki kelas dasar dan tidak ada konstruktor, destruktor, atau metode virtual. Anggota statis tidak menghentikan sesuatu menjadi POD di bawah aturan ini. Aturan ini telah berubah di C ++ 11 dan anggota pribadi tertentu diperbolehkan: Bisakah kelas dengan semua anggota pribadi menjadi kelas POD?3.9 (10): "Tipe aritmatika (3.9.1), tipe enumerasi, tipe pointer, dan pointer ke tipe anggota (3.9.2) dan versi yang memenuhi syarat cv dari tipe ini (3.9.3) adalah tipe skalar pemanggil secara kolektif. Scalar tipe, tipe POD-struct, tipe POD-union (klausa 9), array tipe seperti itu dan versi cv-kualifikasi dari tipe-tipe ini (3.9.3) secara kolektif disebut tipe POD "
9 (4): "Sebuah POD-struct adalah kelas agregat yang tidak memiliki anggota data non-statis dari tipe non-POD-struct, non-POD-union (atau array jenis seperti itu) atau referensi, dan tidak memiliki pengguna- mendefinisikan operator salinan dan tidak ada destruktor yang ditentukan pengguna. Demikian pula serikat-POD adalah serikat agregat yang tidak memiliki anggota data non-statis dari tipe non-POD-struct, non-POD-union (atau array jenis seperti itu) atau referensi, dan tidak memiliki operator penyalin yang ditetapkan pengguna dan tidak ada destruktor yang ditentukan pengguna.
8.5.1 (1): "Agregat adalah array atau kelas (ayat 9) tanpa konstruktor yang dideklarasikan pengguna (12.1), tidak ada anggota data non-statis pribadi atau yang dilindungi (klausa 11), tidak ada kelas dasar (klausa 10) dan tidak ada fungsi virtual (10.3). "
Singkatnya, itu semua built-in tipe data (misalnya int
, char
, float
, long
, unsigned char
, double
, dll) dan semua agregasi data POD. Ya, itu definisi rekursif. ;)
Untuk lebih jelasnya, POD adalah apa yang kita sebut "struct": unit atau sekelompok unit yang hanya menyimpan data.
Seperti yang saya mengerti POD (PlainOldData) hanyalah data mentah - tidak perlu:
Bagaimana cara mengecek apakah ada POD? Nah, ada struct untuk yang disebut std::is_pod
:
namespace std {
// Could use is_standard_layout && is_trivial instead of the builtin.
template<typename _Tp>
struct is_pod
: public integral_constant<bool, __is_pod(_Tp)>
{ };
}
(Dari header type_traits)
Referensi:
Objek POD (data lama biasa) memiliki salah satu dari tipe data ini - tipe fundamental, pointer, union, struct, array, atau kelas - tanpa konstruktor. Sebaliknya, objek non-POD adalah objek yang memiliki konstruktor. Objek POD memulai masa pakainya ketika memperoleh penyimpanan dengan ukuran yang sesuai untuk jenisnya dan masa pakainya berakhir ketika penyimpanan untuk objek tersebut digunakan kembali atau tidak dialokasikan.
Jenis PlainOldData juga tidak boleh memiliki:
Definisi yang lebih longgar dari PlainOldData mencakup objek dengan konstruktor; tetapi mengecualikan mereka dengan apa pun virtual. Masalah penting dengan tipe PlainOldData adalah bahwa mereka non-polimorfik. Warisan dapat dilakukan dengan jenis POD, namun itu hanya boleh dilakukan untuk Implementasi Warisan (penggunaan kembali kode) dan bukan polimorfisme / subtipe.
Definisi umum (meskipun tidak sepenuhnya benar) adalah bahwa tipe PlainOldData adalah sesuatu yang tidak memiliki VeeTable.
Mengapa kita perlu membedakan antara POD dan non-POD sama sekali?
C ++ memulai hidupnya sebagai perpanjangan dari C. Sementara C ++ modern bukan lagi superset C yang ketat, orang masih mengharapkan tingkat kompatibilitas yang tinggi di antara keduanya.
Secara kasar, tipe POD adalah tipe yang kompatibel dengan C dan mungkin sama pentingnya adalah kompatibel dengan optimisasi ABI tertentu.
Agar kompatibel dengan C, kita perlu memenuhi dua kendala.
Fitur C ++ tertentu tidak kompatibel dengan ini.
Metode virtual memerlukan kompiler untuk memasukkan satu atau lebih petunjuk ke tabel metode virtual, sesuatu yang tidak ada di C.
Copy constructor yang ditentukan pengguna, memindahkan constructor, menyalin tugas dan destructor mempunyai implikasi untuk melewati dan mengembalikan parameter. Banyak C ABI lulus dan mengembalikan parameter kecil dalam register, tetapi referensi yang diteruskan ke konstruktor / assigment / destructor yang ditentukan pengguna hanya dapat bekerja dengan lokasi memori.
Jadi ada kebutuhan untuk menentukan tipe apa yang bisa diharapkan "kompatibel dengan C" dan tipe apa yang tidak bisa. C ++ 03 agak terlalu ketat dalam hal ini, setiap konstruktor yang ditentukan pengguna akan menonaktifkan konstruktor bawaan dan setiap upaya untuk menambahkannya kembali akan menghasilkan mereka yang ditetapkan pengguna dan karenanya jenisnya adalah non-pod. C ++ 11 membuka banyak hal, dengan memungkinkan pengguna untuk memperkenalkan kembali konstruktor bawaan.
Contoh semua kasus non-POD dengan static_assert
efek C ++ 11 hingga C ++ 17 dan POD
std::is_pod
ditambahkan dalam C ++ 11, jadi mari kita pertimbangkan standar itu dan seterusnya untuk saat ini.
std::is_pod
akan dihapus dari C ++ 20 sebagaimana disebutkan di https://stackoverflow.com/a/48435532/895245 , mari perbarui ini saat dukungan datang untuk penggantian.
Pembatasan POD menjadi lebih dan lebih santai seiring standar berkembang, saya bertujuan untuk mencakup semua relaksasi dalam contoh melalui ifdefs.
libstdc ++ memiliki sedikit pengujian di: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc tetapi itu terlalu sedikit. Maintainers: tolong gabungkan ini jika Anda membaca posting ini. Saya malas untuk memeriksa semua proyek C ++ testuite yang disebutkan di: /software/199708/is-there-a-compliance-test-for-c-compilers
#include <type_traits>
#include <array>
#include <vector>
int main() {
#if __cplusplus >= 201103L
// # Not POD
//
// Non-POD examples. Let's just walk all non-recursive non-POD branches of cppreference.
{
// Non-trivial implies non-POD.
// https://en.cppreference.com/w/cpp/named_req/TrivialType
{
// Has one or more default constructors, all of which are either
// trivial or deleted, and at least one of which is not deleted.
{
// Not trivial because we removed the default constructor
// by using our own custom non-default constructor.
{
struct C {
C(int) {}
};
static_assert(std::is_trivially_copyable<C>(), "");
static_assert(!std::is_trivial<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// No, this is not a default trivial constructor either:
// https://en.cppreference.com/w/cpp/language/default_constructor
//
// The constructor is not user-provided (i.e., is implicitly-defined or
// defaulted on its first declaration)
{
struct C {
C() {}
};
static_assert(std::is_trivially_copyable<C>(), "");
static_assert(!std::is_trivial<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
}
// Not trivial because not trivially copyable.
{
struct C {
C(C&) {}
};
static_assert(!std::is_trivially_copyable<C>(), "");
static_assert(!std::is_trivial<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
}
// Non-standard layout implies non-POD.
// https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
{
// Non static members with different access control.
{
// i is public and j is private.
{
struct C {
public:
int i;
private:
int j;
};
static_assert(!std::is_standard_layout<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// These have the same access control.
{
struct C {
private:
int i;
int j;
};
static_assert(std::is_standard_layout<C>(), "");
static_assert(std::is_pod<C>(), "");
struct D {
public:
int i;
int j;
};
static_assert(std::is_standard_layout<D>(), "");
static_assert(std::is_pod<D>(), "");
}
}
// Virtual function.
{
struct C {
virtual void f() = 0;
};
static_assert(!std::is_standard_layout<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// Non-static member that is reference.
{
struct C {
int &i;
};
static_assert(!std::is_standard_layout<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// Neither:
//
// - has no base classes with non-static data members, or
// - has no non-static data members in the most derived class
// and at most one base class with non-static data members
{
// Non POD because has two base classes with non-static data members.
{
struct Base1 {
int i;
};
struct Base2 {
int j;
};
struct C : Base1, Base2 {};
static_assert(!std::is_standard_layout<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// POD: has just one base class with non-static member.
{
struct Base1 {
int i;
};
struct C : Base1 {};
static_assert(std::is_standard_layout<C>(), "");
static_assert(std::is_pod<C>(), "");
}
// Just one base class with non-static member: Base1, Base2 has none.
{
struct Base1 {
int i;
};
struct Base2 {};
struct C : Base1, Base2 {};
static_assert(std::is_standard_layout<C>(), "");
static_assert(std::is_pod<C>(), "");
}
}
// Base classes of the same type as the first non-static data member.
// TODO failing on GCC 8.1 -std=c++11, 14 and 17.
{
struct C {};
struct D : C {
C c;
};
//static_assert(!std::is_standard_layout<C>(), "");
//static_assert(!std::is_pod<C>(), "");
};
// C++14 standard layout new rules, yay!
{
// Has two (possibly indirect) base class subobjects of the same type.
// Here C has two base classes which are indirectly "Base".
//
// TODO failing on GCC 8.1 -std=c++11, 14 and 17.
// even though the example was copy pasted from cppreference.
{
struct Q {};
struct S : Q { };
struct T : Q { };
struct U : S, T { }; // not a standard-layout class: two base class subobjects of type Q
//static_assert(!std::is_standard_layout<U>(), "");
//static_assert(!std::is_pod<U>(), "");
}
// Has all non-static data members and bit-fields declared in the same class
// (either all in the derived or all in some base).
{
struct Base { int i; };
struct Middle : Base {};
struct C : Middle { int j; };
static_assert(!std::is_standard_layout<C>(), "");
static_assert(!std::is_pod<C>(), "");
}
// None of the base class subobjects has the same type as
// for non-union types, as the first non-static data member
//
// TODO: similar to the C++11 for which we could not make a proper example,
// but with recursivity added.
// TODO come up with an example that is POD in C++14 but not in C++11.
}
}
}
// # POD
//
// POD examples. Everything that does not fall neatly in the non-POD examples.
{
// Can't get more POD than this.
{
struct C {};
static_assert(std::is_pod<C>(), "");
static_assert(std::is_pod<int>(), "");
}
// Array of POD is POD.
{
struct C {};
static_assert(std::is_pod<C>(), "");
static_assert(std::is_pod<C[]>(), "");
}
// Private member: became POD in C++11
// /programming/4762788/can-a-class-with-all-private-members-be-a-pod-class/4762944#4762944
{
struct C {
private:
int i;
};
#if __cplusplus >= 201103L
static_assert(std::is_pod<C>(), "");
#else
static_assert(!std::is_pod<C>(), "");
#endif
}
// Most standard library containers are not POD because they are not trivial,
// which can be seen directly from their interface definition in the standard.
// /programming/27165436/pod-implications-for-a-struct-which-holds-an-standard-library-container
{
static_assert(!std::is_pod<std::vector<int>>(), "");
static_assert(!std::is_trivially_copyable<std::vector<int>>(), "");
// Some might be though:
// /programming/3674247/is-stdarrayt-s-guaranteed-to-be-pod-if-t-is-pod
static_assert(std::is_pod<std::array<int, 1>>(), "");
}
}
// # POD effects
//
// Now let's verify what effects does PODness have.
//
// Note that this is not easy to do automatically, since many of the
// failures are undefined behaviour.
//
// A good initial list can be found at:
// /programming/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special/4178176#4178176
{
struct Pod {
uint32_t i;
uint64_t j;
};
static_assert(std::is_pod<Pod>(), "");
struct NotPod {
NotPod(uint32_t i, uint64_t j) : i(i), j(j) {}
uint32_t i;
uint64_t j;
};
static_assert(!std::is_pod<NotPod>(), "");
// __attribute__((packed)) only works for POD, and is ignored for non-POD, and emits a warning
// /programming/35152877/ignoring-packed-attribute-because-of-unpacked-non-pod-field/52986680#52986680
{
struct C {
int i;
};
struct D : C {
int j;
};
struct E {
D d;
} /*__attribute__((packed))*/;
static_assert(std::is_pod<C>(), "");
static_assert(!std::is_pod<D>(), "");
static_assert(!std::is_pod<E>(), "");
}
}
#endif
}
Diuji dengan:
for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done
pada Ubuntu 18.04, GCC 8.2.0.
Konsep POD dan tipe sifat std::is_pod
akan ditinggalkan dalam C ++ 20. Lihat pertanyaan ini untuk informasi lebih lanjut.
Dengan C ++, Plain Old Data tidak hanya berarti bahwa hal-hal seperti int, char, dll adalah satu-satunya tipe yang digunakan. Plain Old Data benar-benar berarti dalam praktiknya Anda dapat mengambil struct memcpy dari satu lokasi di memori ke yang lain dan segala sesuatunya akan bekerja persis seperti yang Anda harapkan (yaitu tidak meledak). Ini pecah jika kelas Anda, atau kelas mana pun yang berisi kelas Anda, memiliki sebagai anggota yang merupakan pointer atau referensi atau kelas yang memiliki fungsi virtual. Pada dasarnya, jika pointer harus terlibat di suatu tempat, itu bukan Data Lama Biasa.