c/c++补完计划(四): 字节对齐和虚继承
前言
猪场最爱考的内容, 亲测.
结构体大小
先来看个基础的:
#include <iostream>
#pragma pack (8)
using namespace std;
struct A {
char a;
int b;
double c;
};
int main() {
// 1: 13
// 2: 14
// 4: 16
// 8: 16
cout << sizeof(A) << endl;
}
按理说, char 1个byte, int 4个byte, double 8个byte. 应该是13个字节, 为什么会有别的答案. 甚至没有
#pragma pack (v)
的情况下都是16. 原因就在于字节对齐. 关于字节对齐为啥存在, 简单来说, 就是数据都是一块一块读的, 不是一个一个. 那么如何得出呢, 其实很简单, 1的情况下, 是多少就是多少. 2的话, 补成第一个大于13的2的倍数, 也就是14, 4的话, 第一个大于13的4的倍数, 也就是16, 以此类推. 而64位默认是8字节对齐. 然后你会说, 就这?
类大小
那么下面这个呢?
class B {
public:
B(char a, int b, double c) : a(a), b(b), c(c) {}
static int x;
virtual void hello() {};
private:
char a;
int b;
double c;
};
你可能会说还是16, 因为函数和静态变量不算在大小内. 你答对了一半, 函数是不算, 但是virtual关键字导致了虚指针的产生, 而我的mac是64位, 指针占8个字节, 所以答案是24.
那么继承一下呢?
class C : public B {
public:
C(char a, int b, double c, int d) : B(a, b, c), d(d) {}
virtual void hello() {}
private:
int d;
};
你会说28, 不过注意, 64位默认8字节对齐, 所以是32哦. 你会说, 不对, 这里有virtual, 多一个虚指针. 不对, 这里只有一个虚指针, 继承来的, 指向自己的虚表. 所以如果面试官问你, 为什么基类指针可以动态调用子类函数, 你就可以从虚指针来作答.
虚继承
如果是菱形继承怎么办?
class B {
public:
B(char a, int b, double c) : a(a), b(b), c(c) {}
static int x;
virtual void hello() {};
private:
char a;
int b;
double c;
};
class C : public B {
public:
C(char a, int b, double c, int d) : B(a, b, c), d(d) {}
void hello() override {}
private:
int d;
};
class D : public B {
public:
D(char a, int b, double c, int d) : B(a, b, c), d(d) {}
void hello() override {}
private:
int d;
};
class E : public C, D {
public:
E(char a, int b, double c, int d1, int d2, int d) : C(a, b, c, d1), D(a, b, c, d2), d(d) {}
void hello() override {}
private:
int d;
};
结果64怎么算, 首先B 24, C 28, D 28, E 28 + 28 + 4 = 60, 然后8字节对齐, 64. 如果4字节对齐就是60. 注意, 这里C, D都有虚指针, 被E继承. 如果变化下, 改成虚继承. 先来看输出:
class C : virtual public B {
public:
C(char a, int b, double c, int d) : B(a, b, c), d(d) {}
void hello() override {}
private:
int d;
};
class D : virtual public B {
public:
D(char a, int b, double c, int d) : B(a, b, c), d(d) {}
void hello() override {}
private:
int d;
};
image
是不是懵了, C, D变大了, E变小. 先来看E, 它继承了C, D的独有变量,但是没有继承他们从B得到的, 而是直接从B获取一份内容, 这样就是3个虚指针, B的变量, C和D的变量, 自己的变量, 也就是24+13+4+4+4=49, 8字节对齐, 等于56. 那么40怎么来的? 其实2个虚指针+B的变量+C的变量, 16+13+4=33, 8字节对齐, 40.
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【SpringBoot注解-5】web项目相关注解
- rxjs里的Observable对象如何消费
- 正则表达式入门
- (在模仿中精进数据可视化02) 温室气体排放来源可视化
- ROS机器人URDF建模
- 这是我见过最牛逼的Shell,619行代码!
- 设计模式(五):利用原型模式复制几个葫芦娃
- Vue中数组变动监听
- which命令
- 如何将tensorflow1.x代码改写为pytorch代码(以图注意力网络(GAT)为例)
- tomcat设置好环境变量,依然无法通过cmd startup命令启动
- python调用百度图片识别api
- [Go]GO语言实战-开源WEB客服GO-FLY-gorm下分页的实现
- [Go]GO语言实战-小程序或公众号接口gin框架验证微信服务器消息签名-开源WEB客服
- php一步一步实现mysql协议(一)——抓包本地mysql通信