1. 前言
我们曾经至少四篇帖子或多或少的提到过std::tuple的原理及占用空间大小:
- Modern C++ std::unique_ptr的实现原理
- Modern C++ std::tuple的size
- Modern C++利用工具快速理解std::tuple的实现原理
- GDB调试技巧实战–自动化画出类关系图
但是,当初在第一篇中提出的size的问题还悬而未决,今天我将着重讲这一块,同时也会讲一些tuple的实现代码。
2. 类继承 VS 类has-a
2.1 一般继承与有一个父对象
考虑下面的两片代码:
struct Base {
int i;
};
struct Derived1 : Base
{
int j;
};
struct Base {
int i;
};
struct Derived1
{
Base b;
int j;
};
本质上没有什么区别,第一种是继承Base, 也相当于has a Base。它们的sizeof大小也相同。
加强训练下,猜下下面的程序的输出???:
#include<iostream>
#include <tuple>
using namespace std;
struct Base {
int i;
}; // empty class
struct Derived1 : Base
{
int i;
};
struct Derived2 : Base
{
int i;
};
struct Derived3 : Derived2, Derived1
{
};
int main()
{
Derived3 d;
std::cout<<sizeof(d)<<std::endl;
}
我没有给初始化,所以i的值比较随机,这反而能看出我们真的有4个i, 特别是Base.i有两个:
(gdb) p d
$1 = {
<Derived2> = {
<Base> = {
i = -10544}, i = 32767}, <Derived1> = {
<Base> = {
i = 0}, i = 0}, <No data fields>}
2.2 继承空类 VS 有一个空类对象
但是,如果把Base换成空类哪?如果没有优化,第一种情况还相当于has a Base其大小是1,加上一个int总共8字节(有对其)。但历史没有假设,标准还偏偏允许有优化:Empty base optimization
继承的情况大小为4字节,比has a情况少用4个字节!
2.3 std::tuple中放空类对象 – 继承空类
一言以蔽之:std::tuple正是用了继承而非has a 而节约的空间。
我们举个例子,就举《Modern C++ std::unique_ptr的实现原理》中的例子吧,std::unique_ptr的本质是std::tuple<int*, std::default_delete>, 它的大小是指针的大小8,而不是16. 写个程序验证一下:
#include<iostream>
#include <tuple>
#include <memory>
using namespace std;
int main()
{
int i=5;
std::tuple<int*