#include <iostream> #include <cstdio> #include <queue> #include <vector> #define ll long long using namespace std; /* 注意: 输出不同可能对齐参数或者编译器不同有关。 */ //模板类输出方法 template <class T> int size(T lim) { return sizeof(lim); } /* https://www.cnblogs.com/JingHuanXiao/p/6080726.html class的内存分布: 一个class占的内存分为三部分: 非静态成员变量总合。 加上编译器为了CPU计算,作出的数据对齐处理。 加上为了支持虚函数,产生的额外负担。 抽象类一定有个虚指针,指向存放函数的虚表,在64位下该虚指针占8个字节,32位下占4个字节 */ /*不同编译器可能有所不同,*/ //普通的int,大小是4 class A1 { private: int a; }; //这种排布方式会导致每次都进行对对齐,占用24字节 class A1_5 { private: char c; double d; int a; }; //数据对齐的int,对齐虚指针大小,总共为16 class A2 { private: int a=5; public: virtual void func(){} }; //数据已经被填齐,大小仍为16 class A3 { private: int a; int b; public: virtual void func(){} }; /* 注意: string按照8字节对齐,总共32字节,并且不会变化。 以下为个人推测: vector内部实现为一个类包含3个指针,总字节会输出24,推测string也是个类,内部有4个指针。 所以sizeof(string)以及一些其他的STL,会显示实现的类大小,并且不会因为数据的写入而扩大。 输出48 */ class A4 { private: string a; int b; public: virtual void func(){} }; /* static保存在静态存储区(存储全局变量和静态变量, 这些变量的空间在程序编译时就已经分配好了) 大小为4 */ class A5 { private: int a; static int sta; }; /*非抽象类的函数不占用类空间,大小为4*/ class A6 { private: int a=114514; static int b; //不绑定this,C++规定不能在类中初始化static,默认初始化为0(部分编译不初始化报错) void print(){cout<<a<<" is here"<<endl;} public: static int c; void func(){cout<<"I am here"<<endl;} //可以用空指针调用 void func2(){cout<<a<<" I am here"<<endl;} //绑定了this指针,不能用空指针调用,但是可以判断this==null然后跳过 //void func2(){if(this == nullptr) return ;cout<<a<<" I am here"<<endl;} static void fun2(){b++;cout<<b<<" is running here"<<endl;} //static函数只能调用static变量 static void fun3(){cout<<c<<" is running here"<<endl;} static int fun1(){return b;} static void funin(){A6 limm; limm.print();} //可以作为一个private接口调用内部函数 }; int A6::b = 0; //使用域操作符指定类域的方法获得并初始化 int A6::c = 0; /*--------------------------单继承----------------------*/ /*A2的数据没有对齐,编译期自动对齐,被B1继承后,B1的int将其对齐,虚指针大小不变,输出为16,单继承子类复用指针*/ //https://blog.csdn.net/lunaticzhg/article/details/115549909 /*显然和这篇文章不太一样,个人认为这里是由于先放指针,再放int从而导致两个int对齐了要求,而文中是32位,看不出差别*/ /*不过比较明确的是继承后父类的同名对象没有消失,而是被隐藏了*/ class B1 : public A2 { private: int a; public: virtual void func(){} }; // 未对齐后变为24 class B2 : public A3 { private: int b; public: virtual void func(){} }; /* 虚继承会传入一个虚基指针,对应虚基表,虚继承优先放入基类指针和子类,然后是父指针和父对象 此处B3会对齐为16,继承的A2也会,输出32 此时父类就是基类 */ class B3 : virtual public A2 { private: int b=3; public: virtual void func(){} }; class B4 : virtual public A2 { private: int b=4; public: virtual void func(){} }; /*--------------------多继承-----------------------------*/ /* 继承所有父类指针,优先逐个放入父类,再放入子类 A2自动补齐是16,A3是16,再加上自己补齐的8 输出40 */ class C1 :public A2,public A3 { private: int c; public: virtual void func(){} }; /*--------------菱形继承------------------------*/ /* 优先放入直接两个基类虚指针(继承自父类)、父指针和直接父类对象,然后是子类对象,最后基类对象 虚拟继承的子类如果有新定义的虚函数,内部还是会有自己的虚函数表指针,也就是说虽然只有一个基类,但是子类却还是会有两个虚函数表指针,十分复杂。 “我的建议是,不要再一个virtual base class中声明nonstatic data members。如果这么做,你会距离复杂的深渊愈来愈近,终不可拔。” */ class D1 : public B3,public B4 { private: int b=100; public: virtual void func(){} }; int main() { /*----------------一般的类内存问题----------------*/ A1 a1; cout<<size(a1)<<endl; A1_5 a105; cout<<size(a105)<<endl; A2 a2; cout<<size(a2)<<endl; A3 a3; cout<<size(a3)<<endl; A4 a4; cout<<size(a4)<<endl; A5 a5; cout<<size(a5)<<endl; /*---------------类中的static探究----------------------*/ A6 *a6 = new A6; cout<<size(*(a6))<<endl; delete a6; a6 = nullptr; a6->fun2(); a6->func(); //可以发现即使是空指针也可以执行 //a6->func2(); //段错误 a6->c++; //空指针也能调用 a6->fun3(); //a6->b++; //不能从外部调用private类型 a6->funin(); /*---------------单继承的类的大小探究---------------------*/ B1 b1; cout<<size(b1)<<endl; B2 b2; cout<<size(b2)<<endl; B3 b3; cout<<size(b3)<<endl; cout<<endl<<endl; /*---------------多继承的类的大小探究---------------------*/ C1 c1; cout<<size(c1)<<endl; cout<<endl<<endl; /*---------------菱形继承的类的大小探究---------------------*/ D1 d1; cout<<size(d1)<<endl; return 0; }
标签:int,a6,private,memory,测试代码,对齐,抽象类,class,指针 From: https://www.cnblogs.com/ztlsw/p/17341109.html