Tôi đã gặp thuật ngữ này loại POD một vài lần.
Nó có nghĩa là gì?
Tôi đã gặp thuật ngữ này loại POD một vài lần.
Nó có nghĩa là gì?
Câu trả lời:
POD là viết tắt của Plain Old Data - nghĩa là một lớp (cho dù được xác định bằng từ khóa struct
hoặc từ khóa class
) mà không có hàm tạo, hàm hủy và hàm thành viên ảo. Bài viết của Wikipedia về POD đi sâu vào chi tiết hơn một chút và định nghĩa nó là:
Cấu trúc dữ liệu cũ đơn giản trong C ++ là một lớp tổng hợp chỉ chứa PODS là thành viên, không có hàm hủy do người dùng định nghĩa, không có toán tử gán sao chép do người dùng định nghĩa và không có thành viên không thuộc loại con trỏ thành thành viên.
Chi tiết hơn có thể được tìm thấy trong câu trả lời này cho C ++ 98/03 . C ++ 11 đã thay đổi các quy tắc xung quanh POD, giúp chúng thư giãn rất nhiều, do đó cần có câu trả lời tiếp theo ở đây .
POD là một loại (bao gồm các lớp) trong đó trình biên dịch C ++ đảm bảo rằng sẽ không có "phép thuật" nào xảy ra trong cấu trúc: ví dụ: các con trỏ ẩn cho vtables, offset được áp dụng cho địa chỉ khi nó được truyền sang các loại khác ( ít nhất là nếu POD của mục tiêu cũng vậy), các hàm tạo hoặc hàm hủy. Nói một cách đơn giản, một loại là một POD khi những thứ duy nhất trong đó là các loại tích hợp và kết hợp chúng. Kết quả là một cái gì đó "hoạt động như" một loại C.
int
, char
, wchar_t
, bool
, float
, double
Là PODs, cũng như long/short
và signed/unsigned
các phiên bản của họ.enums
là PODconst
hoặc volatile
POD là POD.class
, struct
hoặc union
của POD là một POD với điều kiện là tất cả các thành viên dữ liệu không tĩnh public
và nó không có lớp cơ sở và không có hàm tạo, hàm hủy hoặc phương thức ảo. Các thành viên tĩnh không dừng một cái gì đó là POD theo quy tắc này. Quy tắc này đã thay đổi trong C ++ 11 và một số thành viên riêng được phép: Một lớp có tất cả các thành viên riêng có thể là lớp POD không?3.9 (10): "Các loại số học (3.9.1), các kiểu liệt kê, các loại con trỏ và con trỏ tới các loại thành viên (3.9.2) và các phiên bản đủ điều kiện cv của các loại này (3.9.3) là các kiểu vô hướng gọi chung. các loại, các loại cấu trúc POD, các loại kết hợp POD (điều 9), các mảng của các loại đó và các phiên bản đủ điều kiện cv của các loại này (3.9.3) được gọi chung là các loại POD "
9 (4): "POD-struct là một lớp tổng hợp không có thành viên dữ liệu không tĩnh thuộc loại không POD-struct, non-POD-union (hoặc mảng của các loại đó) hoặc không có người dùng- xác định toán tử sao chép và không có hàm hủy do người dùng định nghĩa. Tương tự, POD-union là một tập hợp tổng hợp không có thành viên dữ liệu không tĩnh thuộc loại không POD-struct, non-POD-union (hoặc mảng của các loại đó) hoặc tham chiếu, và không có toán tử sao chép do người dùng định nghĩa và không có hàm hủy do người dùng định nghĩa.
8.5.1 (1): "Tập hợp là một mảng hoặc lớp (mệnh đề 9) không có hàm tạo do người dùng khai báo (12.1), không có thành viên dữ liệu không tĩnh riêng tư hoặc được bảo vệ (mệnh đề 11), không có lớp cơ sở (mệnh đề 10) và không có chức năng ảo (10.3). "
Nói tóm lại, đó là tất cả built-in các kiểu dữ liệu (ví dụ như int
, char
, float
, long
, unsigned char
, double
, vv) và tất cả các tập hợp dữ liệu POD. Vâng, đó là một định nghĩa đệ quy. ;)
Nói rõ hơn, POD là cái mà chúng ta gọi là "struct": một đơn vị hoặc một nhóm các đơn vị chỉ lưu trữ dữ liệu.
Theo tôi hiểu, POD (PlainOldData) chỉ là dữ liệu thô - không cần:
Làm thế nào để kiểm tra nếu một cái gì đó là POD? Vâng, có một cấu trúc cho cái gọi là 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)>
{ };
}
(Từ tiêu đề type_traits)
Tài liệu tham khảo:
Một đối tượng POD (dữ liệu cũ đơn giản) có một trong các loại dữ liệu này - một kiểu cơ bản, con trỏ, liên kết, struct, mảng hoặc lớp - không có hàm tạo. Ngược lại, một đối tượng không phải POD là một đối tượng mà hàm tạo tồn tại. Một đối tượng POD bắt đầu thời gian tồn tại của nó khi nó có được lưu trữ với kích thước phù hợp với loại của nó và thời gian tồn tại của nó kết thúc khi việc lưu trữ cho đối tượng được sử dụng lại hoặc giải phóng.
Các loại PlainOldData cũng không được có bất kỳ:
Một định nghĩa lỏng lẻo hơn của PlainOldData bao gồm các đối tượng với các hàm tạo; nhưng loại trừ những người có bất cứ điều gì ảo. Vấn đề quan trọng với các loại PlainOldData là chúng không đa hình. Kế thừa có thể được thực hiện với các loại POD, tuy nhiên, nó chỉ nên được thực hiện cho Mã hóa triển khai (tái sử dụng mã) và không phải là đa hình / phân nhóm.
Một định nghĩa phổ biến (mặc dù không hoàn toàn chính xác) là loại PlainOldData là bất cứ thứ gì không có VeeTable.
Tại sao chúng ta cần phân biệt giữa POD và không phải POD?
C ++ bắt đầu cuộc sống như một phần mở rộng của C. Trong khi C ++ hiện đại không còn là siêu bộ nghiêm ngặt của C, mọi người vẫn mong đợi mức độ tương thích cao giữa hai loại.
Nói một cách đơn giản, một loại POD là một loại tương thích với C và có lẽ quan trọng không kém là tương thích với các tối ưu hóa ABI nhất định.
Để tương thích với C, chúng ta cần thỏa mãn hai ràng buộc.
Một số tính năng C ++ không tương thích với điều này.
Các phương thức ảo yêu cầu trình biên dịch chèn một hoặc nhiều con trỏ vào các bảng phương thức ảo, một cái gì đó không tồn tại trong C.
Các hàm tạo sao chép do người dùng định nghĩa, các hàm tạo di chuyển, các phép gán sao chép và các hàm hủy có ý nghĩa cho việc truyền và trả về tham số. Nhiều C ABI vượt qua và trả về các tham số nhỏ trong các thanh ghi, nhưng các tham chiếu được truyền cho hàm tạo / xác định / hàm hủy do người dùng xác định chỉ có thể hoạt động với các vị trí bộ nhớ.
Vì vậy, cần phải xác định loại nào có thể được dự kiến là "tương thích C" và loại nào không thể. C ++ 03 có phần quá khắt khe về vấn đề này, bất kỳ hàm tạo nào do người dùng xác định sẽ vô hiệu hóa các hàm tạo tích hợp và mọi nỗ lực để thêm chúng trở lại sẽ dẫn đến chúng là do người dùng định nghĩa và do đó loại không phải là nhóm. C ++ 11 đã mở ra mọi thứ khá nhiều, bằng cách cho phép người dùng giới thiệu lại các hàm tạo tích hợp.
Ví dụ về tất cả các trường hợp không phải POD với các hiệu ứng static_assert
từ C ++ 11 đến C ++ 17 và POD
std::is_pod
đã được thêm vào C ++ 11, vì vậy bây giờ chúng ta hãy xem xét tiêu chuẩn đó.
std::is_pod
sẽ bị xóa khỏi C ++ 20 như được đề cập tại https://stackoverflow.com/a/48435532/895245 , hãy cập nhật thông tin này khi có hỗ trợ thay thế.
Các hạn chế POD đã trở nên ngày càng thoải mái hơn khi tiêu chuẩn phát triển, tôi hướng đến việc bao gồm tất cả các thư giãn trong ví dụ thông qua ifdefs.
libstdc ++ có rất ít thử nghiệm tại: https://github.com/gcc-mirror/gcc/blob/gcc-8_2_0-release/libstdc%2B%2B-v3/testsuite/20_util/is_pod/value.cc nhưng nó chỉ là quá ít Người bảo trì: vui lòng hợp nhất điều này nếu bạn đọc bài viết này. Tôi lười kiểm tra tất cả các dự án thử nghiệm C ++ được đề cập tại: /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
}
Đã thử nghiệm với:
for std in 11 14 17; do echo $std; g++-8 -Wall -Werror -Wextra -pedantic -std=c++$std pod.cpp; done
trên Ubuntu 18.04, GCC 8.2.0.
Khái niệm POD và đặc điểm loại std::is_pod
sẽ không được chấp nhận trong C ++ 20. Xem câu hỏi này để biết thêm thông tin.
Với C ++, Plain Old Data không có nghĩa là những thứ như int, char, v.v. là những loại duy nhất được sử dụng. Plain Old Data thực sự có nghĩa là trong thực tế, bạn có thể lấy một cấu trúc ghi nhớ nó từ một vị trí trong bộ nhớ sang một vị trí khác và mọi thứ sẽ hoạt động chính xác như bạn mong đợi (nghĩa là không nổ tung). Điều này phá vỡ nếu lớp của bạn, hoặc bất kỳ lớp nào mà lớp của bạn chứa, có một thành viên là con trỏ hoặc tham chiếu hoặc lớp có chức năng ảo. Về cơ bản, nếu con trỏ phải được tham gia ở đâu đó, thì đó không phải là Dữ liệu cũ đơn giản.