文章

POD、trivial copyable,standard layout

1. trival copyable

trival copyable 是指内存布局可以使用 memcpy 进行内存拷贝的类型,但不定义与C类型兼容。

C++11 要求如下:

  1. 构造函数,复制构造函数,移动构造函数(move ctor),析构函数,均为默认。
  2. 赋值运算符,移动赋值运算符,均为默认。
  3. 没有虚函数虚基类

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 进行授权