谈到struct和class的区别,我们的第一反应就是struct也可以和class一样拥有多态,继承的特性,最大的不同只是他们的默认权限不同,struct默认访问权限是public,class默认权限是private,但其实还有一点:memory layout可能不同。
struct(没有使用虚函数)的数据成员声明顺序和这些数据成员在内存中的offset一定是一致的。而class只有在POD原则(Plain Old Data)下才与struct的memory layout一致。
那么问题来了什么是满足POD原则的class?
POD数据类型
概念
POD数据类型主要用来解决C++与C之间数据类型的兼容性,以实现C++程序与C函数的交互。
- POD类型(plain old data),与C兼容的原始数据,例如,结构体和整型等C语言中的类型是POD类型,而带有用户自定义的构造函数或虚函数的类则不是(指针类型也是POD类型)
- string t_obj1(“cppcourase”); //错误,不能调用对象的构造函数
- string* t_obj2 = new string; //错误,初始化只能是编译期常量
- string* t_obj3 = NULL; //正确
什么情况下满足POD原则
C++11中把POD分为了两个基本概念的集合,即:平凡的(trival)和标准布局的(standard layout)。只有满足这两个基本概念才能称为是POD类型。
- 拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor)
- 拥有平凡的复制构造函数(trivial copy constructor)和移动构造函数(trivial move constructor)
- 拥有平凡的复制赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator)
- 不能包含虚拟函数和虚拟基类
trivial指由编译器默认提供的,而不是用户自定义的
一个standard layout的class或者struct应该符合以下定义:
- 没有 non-standard-layout类型(或这些类型的数组)和引用的非静态数据成员
- 没有虚函数和虚基类
- 非静态数据成员的访问控制必须是相同的
- 没有non-standard-layout的基类
- 在class或者struct继承时,满足以下两种情况之一的class或者struct也是标准布局的:
- 派生类中有非静态成员,且只有一个仅包含静态成员的基类
- 基类有非静态成员,而派生类没有非静态成员
- 相同基类类型的非静态数据成员不能作为第一个成员
示例:
// 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