Lecture
Plain data structure, passive data structure- passive data structure (PDS, also termed a plain old data structure, or plain old data, POD),— in modern high-level programming languages, a data type that has a rigidly defined layout of fields in memory, requires no access restriction and no automatic management. Variables of this type can be copied with simple memory-block copying procedures such as memcpy. It is a data structure represented only as a passive collection of field values (instance variables) without using object-oriented features. Its opposite is a managed data structure.
The easiest way to define a plain data structure is by contradiction. If the compiler silently rearranges the fields without the user's knowledge, or silently calls a constructor when the data structure is created, or calls a destructor when it is destroyed, or a special copy procedure when it is copied, then this is a managed (that is, not plain) structure.
Passive data structures are suitable when there is a part of the system where it must be clearly indicated that the detailed logic for manipulating the data and its integrity resides elsewhere. PDS are often found at the boundaries of a system, where information moves to or from other systems or to persistent storage, and the domain logic found in other parts of the system is irrelevant. For example, a PDS would be convenient for representing the field values of objects that are built from external data, in a part of the system where the semantic checks and interpretations required for valid objects have not yet been applied.
Plain data structures have two distinguishing features.
The compiler can automatically rearrange a data structure at its own discretion (for example, change the order of fields. In C++ this is possible only if there is an access label public/private/protected between the fields. A sequence of fields not separated by such a label must be laid out in memory in the order the fields were declared). Such a rearrangement can significantly save memory, but it breaks compatibility. In POD's this optimization is disabled.
In other words: types marked as POD are laid out in memory exactly as the programmer described (possibly with some alignment). Therefore only POD's can be used for communication between two runtime libraries. In particular — for passing data from program to program, from plugin to plugin, for communicating with code written in another programming language. To quickly write a complex file header such as a BMP header to disk, you can build it in memory and then write it out with a single command — but the data structure in which we build the header must also be a POD.
This means that when an object is created there is no need to call a constructor, when it is copied — an assignment operation, and when it is destroyed — a destructor. This, in turn, gives the following advantages:
In C++, POD is defined by contradiction. A data type is a POD if:
According to the C++ standard, a plain data type is laid out in memory exactly as described (and is fully byte-for-byte compatible in memory layout with a C structure). A managed structure, however, can be reorganized by the compiler however it deems most efficient.
Definition of POD before C++11:
An aggregate is either an array or a class that does not have:
An aggregate can be initialized (as in C) with a list of the form = {1, 2, 3};
A scalar is:
(that is, a type that is not a class, an array, or a reference)
A POD is either a scalar, or an array of other PODs, or a class that is an aggregate, and in addition:
«Predictable layout in memory» and «absence of management code» — are similar but different properties of a type. For example, the STRRET data structure, which in Windows is used to pass strings from one memory manager to another, can be «wrapped» in management code, but the second property — predictable layout — remains. Therefore the concept of PODs in C++11 is split into three.
A class is called "having a trivial copy constructor" if all of the following are true:
An auto-generated trivial copy constructor is memmove().
The concepts of "having a trivial default constructor/assignment operator/move constructor/move operator" are defined in exactly the same way.
A class is called "having a trivial destructor" if all of the following are true:
Such a class requires no destruction, and the memory holding it can be freed without cleanup.
A class is called "trivially copyable" if all of the special member functions listed above are trivial for it (except the default constructor, which may be non-trivial). Scalars, as well as arrays of trivially copyable objects, are also trivially copyable. Such types can be copied via memcpy.
A class is called "trivial" if it is trivially copyable and, in addition, its default constructor is trivial.
In other words, a class is trivial if the following are trivial for it:
A class is a standard-layout type if:
Let us explain the last condition: in the language, there cannot be two different objects of the same type with the same address, from which it follows that the size of an empty class (with no non-static fields) cannot be 0 (it is at least 1). However, an exception is made for "part B in the class class D : B", and its size (if it is empty) can be exactly zero, which results in there being no "padding" between the start of D and its first field. But if the type of the first field is also B, the exception cannot apply, because (B*)&d and &(d.field1) point to different objects of the same type, and therefore "padding" is needed. The last condition in the list above means nothing more than "in standard-layout classes such padding is forbidden".
Such types have a predictable layout in memory (for example, the address of the object as a whole coincides with the address of its first field, naturally after a reinterpret_cast to the same type, for example void*), and they can be passed to another runtime library and to other programming languages.
Then a POD — is an array of other PODs, or a scalar, or a trivial standard-layout class all of whose non-static fields are also PODs.
For working with constants computed at compile time, and static initialization, C++11 has a softer notion — a literal type. Namely:
Starting with C++03, there is a difference between the statements T t; and T t();, as well as between new T and new T().
The version with empty parentheses is called "value initialization", and the one without them is called "default initialization".
Default initialization: if the default constructor is trivial, then nothing is done, and the object contains garbage. If the default constructor is non-trivial, it is executed.
Value initialization: if there is an explicitly written default constructor, it is executed. If there is not (that is, if the default constructor is trivial or is generated automatically), then the object is first zeroed out, and only then is the constructor executed (if it is non-trivial). Scalar types are zeroed out during value initialization.
All types are considered plain data structures except for:
In Java, some developers believe that the PDS concept corresponds to a class with public data members and no methods (Java Code Conventions 10.1), , i.e. a data transfer object. Others also include plain old Java objects (POJO), a class that has methods, but only getter and setter methods, without logic, and JavaBeans fall under the PDS concept if they do not use event handling and do not implement any methods beyond getters and setters. However, POJOs and Java Beans have encapsulation and therefore violate the fundamental definition of a PDS.
In PHP, associative arrays and stdClass objects can be regarded as PDS.
Other representations of structured data, such as XML or JSON, can also be used as PDS, provided no significant semantic constraints are used.
Comments