POD、trivial copyable,standard layout
1. trival copyable
trival copyable
是指内存布局可以使用 memcpy
进行内存拷贝的类型,但不定义与C
类型兼容。
C++11
要求如下:
- 构造函数,复制构造函数,移动构造函数(move ctor),析构函数,均为默认。
- 赋值运算符,移动赋值运算符,均为默认。
- 没有虚函数和虚基类。
1.1 说明
- 默认的构造等函数,包含可以使用
=default
,如果包含自定义的构造函数,且提供=default
的构造函数,其依然是trival copyable
。 - 可以使用初始化列表初始化成员,其依然是
trival copyable
。 - 如果包含了
虚函数
/虚基类
/虚析构函数
,因为会生成vptr
,所以不是trival copyable
。 - 可以包含
static
成员,且可以是None trivial copyable
。 - 可以包含
private
等访问控制符。 C++11
中,我们使用模版类std::is_trivially_copyable<T>::value
来判断一个类型是否为平凡类型。
1.2 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// empty classes are trivial
struct Trivial1 {};
// all special members are implicit
struct Trivial2 {
int x;
};
struct Trivial3 : Trivial2 { // base class is trivial
Trivial3() = default; // not a user-provided ctor
int y;
};
struct Trivial4 {
public:
int a;
private: // no restrictions on access modifiers
int b;
};
struct Trivial5 {
Trivial1 a;
Trivial2 b;
Trivial3 c;
Trivial4 d;
};
struct Trivial6 {
Trivial2 a[23];
};
struct Trivial7 {
Trivial6 c;
void f(); // it's okay to have non-virtual functions
};
struct NonTrivial1 : Trivial3 {
virtual void f(); // virtual members make non-trivial ctors
};
struct Trivial8 {
int x;
static NonTrivial1 y; // no restrictions on static members
};
struct Trivial9 {
Trivial9() = default; // not user-provided
// a regular constructor is okay because we still have default ctor
Trivial9(int x) : x(x) {};
int x;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct NonTrivial2 {
NonTrivial2() : z(42) {} // user-provided ctor
int z;
};
struct NonTrivial3 {
NonTrivial3(); // user-provided ctor
int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
// still counts as user-provided
struct NonTrivial5 {
virtual ~NonTrivial5(); // virtual destructors are not trivial
};
2. standard layout
standard layout
是指定义数据结构成员的内存布局规则,使其可以与其他语言交互,如可以使用 extern "C"
进行类型声明。
- 没有虚函数,没有虚基类。
- 所有非静态成员都有相同的访问控制。
- 非静态成员不能是引用类型。
- 如果有基类,第一个非静态成员类型不能是其基类类型。
- 如果有基类,要求基类也是
standard layout
。 - 继承树中,只能有一个类有非静态数据成员。
2.1 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// empty classes have standard-layout
struct StandardLayout1 {};
struct StandardLayout2 {
int x;
};
struct StandardLayout3 {
private: // both are private, so it's ok
int x;
int y;
};
struct StandardLayout4 : StandardLayout1 {
int x;
int y;
void f(); // perfectly fine to have non-virtual functions
};
struct StandardLayout5 : StandardLayout1 {
int x;
StandardLayout1 y; // can have members of base type if they're not the first
};
struct StandardLayout6 : StandardLayout1, StandardLayout5 {
// can use multiple inheritance as long only
// one class in the hierarchy has non-static data members
};
struct StandardLayout7 {
int x;
int y;
StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};
struct StandardLayout8 {
public:
StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
int x;
};
struct StandardLayout9 {
int x;
static NonStandardLayout1 y; // no restrictions on static members
};
struct NonStandardLayout1 {
virtual f(); // cannot have virtual functions
};
struct NonStandardLayout2 {
NonStandardLayout1 X; // has non-standard-layout member
};
struct NonStandardLayout3 : StandardLayout1 {
StandardLayout1 x; // first member cannot be of the same type as base
};
struct NonStandardLayout4 : StandardLayout3 {
int z; // more than one class has non-static data members
};
struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class
参考资料:
附加:类对象的生命周期经历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <memory>
template <typename T>
void life_of_an_object
{
std::allocator<T> alloc;
// 1. 通过 allocator 抑或是 malloc 等其他方式分配出空间
T * p = alloc.allocate(1);
// 2. 通过 placement new,(在需要的时候) 动态地构造对象
new (p) T(); // 这里是默认构造,也可能是带参数的构造方式如 new (p) T(构造参数...);
// 3. 通过显式调用析构函数,(在需要的时候) 动态地销毁对象
p->~T();
// 4. 通过分配函数的对应的解分配手段,解分配空间
alloc.deallocate(p, 1);
}
本文由作者按照 CC BY 4.0 进行授权