C++中的POD类型
POD类型是C++中常见的概念,用来说明类/结构体的属性,具体来说它是指没有使用面相对象的思想来设计的类/结构体。POD的全称是Plain Old Data,Plain表明它是一个普通的类型,没有虚函数虚继承等特性;Old表明它与C兼容。
POD类型在C++中有两个独立的特性:
- 支持静态初始化(static initialization)
- 拥有和C语言一样的内存布局(memory layout)
这两个特性分布对应两个概念:trivial classes和standard-layout。现在提起POD类型通常是指有这两个特性的类,且这个类的非静态成员也是POD类型。
Trivial classes
trivial classes支持静态初始化(static initizlization),如果一个类包含以下特点,那么它就是trivial class:
- 所有的拷贝构造函数都是trivial
- 所有的移动构造函数都是trivial
- 所有鹅赋值操作符都是trivial
- 所有的移动赋值操作符都是trivial
- 默认构造函数和析构函数是trivial
这里说的trivial构造函数是编译器生成的构造函数,而不是用户自定义的;且它的基类也有这样的特性。C++11中的模版template <typename T> struct std::is_trivial
可判断类是否是trivial。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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86#include <iostream>
// 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 Trivial8 {
Trivial8() = default; // not user-provided
// a regular constructor is okay because we still have default ctor
Trivial8(int x) : x(x) {};
int x;
};
struct NonTrivial1 : Trivial3 {
virtual void f(); // virtual members make non-trivial ctors
};
struct Trivial9 {
int x;
static NonTrivial1 y; // no restrictions on static members
};
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 NonTrivial4 {
virtual ~NonTrivial4(); // virtual destructors are not trivial
};
int main(int argc, char* argv[]) {
std::cout << std::is_trivial<Trivial1>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial2>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial3>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial4>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial5>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial6>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial7>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial8>::value << std::endl; // 1
std::cout << std::is_trivial<Trivial9>::value << std::endl; // 1
std::cout << std::is_trivial<NonTrivial1>::value << std::endl; // 0
std::cout << std::is_trivial<NonTrivial2>::value << std::endl; // 0
std::cout << std::is_trivial<NonTrivial3>::value << std::endl; // 0
std::cout << std::is_trivial<NonTrivial4>::value << std::endl; // 0
return 0;
}
Standard-layout
这里的standard是指可以和其他语言通信,因为standard-lay类型的内部布局和C结构体一样。Standard layout定义如下:
- 所有非静态成员都是standard-layout
- 没有虚函数和虚基类
- 非静态成员访问控制权一样
- 基类是standard-lay
- 没有静态成员变量,或者在整个继承树中,只有一个类有静态成员变量。
- 第一个非静态成员不是基类
C++11中可以使用template <typename T>struct std::is_standard_layout
判断一个类是否是standard-layout。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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88#include <iostream>
// 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 NonStandardLayout1 {
virtual void f() {}; // cannot have virtual functions
};
struct StandardLayout9 {
int x;
static NonStandardLayout1 y; // no restrictions on static members
};
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
int main(int argc, char* argv[]) {
std::cout << std::is_standard_layout<StandardLayout1>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout2>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout3>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout4>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout5>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout6>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout7>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout8>::value << std::endl; // 1
std::cout << std::is_standard_layout<StandardLayout9>::value << std::endl; // 1
std::cout << std::is_standard_layout<NonStandardLayout1>::value << std::endl; // 0
std::cout << std::is_standard_layout<NonStandardLayout2>::value << std::endl; // 0
std::cout << std::is_standard_layout<NonStandardLayout3>::value << std::endl; // 0
std::cout << std::is_standard_layout<NonStandardLayout4>::value << std::endl; // 0
std::cout << std::is_standard_layout<NonStandardLayout5>::value << std::endl; // 0
return 0;
}
优点
POD类型相对非POD类型有以下优点:
1、字节赋值。POD类型变量可以不使用构造函数、赋值操作符赋值,直接通过memset()
、memcpy()
初始化赋值。
2、兼容C内存布局。C++程序可以和C进行交互,或者可以和其他语言交互。
3、保证静态初始化安全有效。静态初始化很多时候可以提高程序性能,POD类型初始化更加简单。
参考
what-are-aggregates-and-pods-and-how-why-are-they-special
《深入理解C++11》