类的memory以及抽象类继承学习(含测试代码)

#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;
}

 

posted @ 2023-04-21 17:09  ztlsw  阅读(15)  评论(0编辑  收藏  举报