• 空类实例化


    昨天面试问到几个问题,当时感觉不是很了解,回来之后整理下,先说一下空类的实例化问题

    一个C++空类实例化大小是多少?

    一个C++空类实例化大小事实上并不为空,他有一个隐晦的1个byte.

    首先:什么是类的实例化?

    所谓类的实例化,就是在内存中分配一块地址。

    例一:

    #include<iostream>
    using namespace std;
    
    class A {};
    class B {};
    class C : public A {
        virtual void c_fun()=0;
    };
    class D : public B, public C {};
    
    int main()
    {
        cout<<"sizeof(A):"<<sizeof(A)<<endl;
        cout<<"sizeof(B):"<<sizeof(B)<<endl;
        cout<<"sizeof(C):"<<sizeof(C)<<endl;
        cout<<"sizeof(D):"<<sizeof(D)<<endl;
        return  0;
    }

    输出:

    sizeof(A):1
    sizeof(B):1
    sizeof(C):4
    sizeof(D):8
    请按任意键继续. . .

    为什么会出现这种结果呢?

    类A和类B明明都是空类,它的大小应该为0,为什么编译器输出的结果为1呢?这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加1个字节,这样空类在实例化之后再内存中得到了独一无二的地址,所以A,B的大小为为1。

    类C是由类A派生而来,它里面有一个虚函数,由于有一个虚函数,就有一个指向虚函数表(虚表)的指针*_vptr,在32位的系统中分配给指针的大小为4个字节,所以最后得到C类的大小为4。

    类D是由类B和类C派生而来的,它的大小应该为二者之和为5,为什么确是8呢?这是因为为了提高实例在内存中的存取效率,类的大小往往被调整到系统的整数倍,采取对长补齐,采用就近最近的整数倍数,就是该类的大小,所以D类的大小为8个字节。

    当然,在不同的编译器上上述的结果可能不同。

    例二:

    #include<iostream>
    using namespace std;
    
    class A{
    private: 
        int data;
    };
    
    class B{ 
    private:
        int data;
        static int data1;
    };
    int B::data1=0;
    
    void main()
    {
        cout<<"sizeof(A) = "<< sizeof(A) <<endl;
        cout<<"sizeof(B) = "<< sizeof(B) <<endl;
    }
    输出:
    sizeof(A) = 4
    sizeof(B) = 4
    请按任意键继续.

    为什么类B多了一个数据成员,大小却和类A相同呢?

    这是因为类B的静态成员数据被编译器放在程序的global data segment中,它是类的一个数据成员,但是它不影响类的大小,不管这个类实际产生了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态成员数据只有被实例化的时候,它们才存在。但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在,可以这么说,类的静态数据是一种特殊的全局变量。所以类A和类B的大小相同。

    例三:

    现在看一个有构造函数和析构类的大小:

    #include<iostream>
    using namespace std;
    
    class A{
    public :
        A(int a)
        {
            a=x;
        }
        void f(int x)
        {
            cout<<x<<endl;
        }
        ~A(){}
    
    private:
        int x;
        int g;
    };
    class B{
    public:
    private:
        int data;
        int data2;
        static int xs;
    };
    int B::xs=0;
    
    void main()
    {
        A s(10);
        s.f(10);
        cout << "sizeof(a): "<< sizeof(A) << endl;
        cout << "sizeof(b): "<< sizeof(B) << endl;
    }
    输出:
    10
    sizeof(a): 8
    sizeof(b): 8
    请按任意键继续. . .

    它们的结果均相同,可以看出类的大小与它当中的构造函数、析构函数,以及其他的成员函数无关,只与它当中的成员数据有关。

    例四:

    #include<iostream>
    using namespace std;
    
    class X {};
    class Y : public virtual X {};
    class Z : public virtual X {};
    class A : public Y, public Z {};
    // 上述为经典的钻石继承
    
    int main()
    {
        cout << "sizeof(X): " << sizeof(X) << endl;
        cout << "sizeof(Y): " << sizeof(Y) << endl;
        cout << "sizeof(Z): " << sizeof(Z) << endl;
        cout << "sizeof(A): " << sizeof(A) << endl;
    
        return 0;
    }

    输出:

    sizeof(X): 1
    sizeof(Y): 4
    sizeof(Z): 4
    sizeof(A): 8
    请按任意键继续. .

    和例一一样,类X是一个空类,但它事实上并不是空的,它有一个隐晦的1字节,那是被编译器安插进去的1个char,它主要的作用是使这个类的对象在内存中有一个独一无二的地址。

    类Y和类Z的大小受三个因素的影响:

    1. 语言本身所造的额外的负担。因为类Y和类Z都是公有继承于类X,这里将会有vptr,在32位上为4字节;
    2. 编译器对特殊情况所作的优化处理。这里做了优化,一个空的虚基类被放在了派生类的最开头部分,也就是说它未花费任何额外的空间,这就节省了类X的1 byte;如果没做优化,一个空的虚基类会放在派生类的固定不变动的尾端;
    3. Alingment对长补齐的限制,对长补齐,上述未做优化,则虚基类X的1个字节与类Y的4个字节(共5个字节),补齐3个字节,最终为8个字节;如果做优化,则虚基类X的1个字节将被省略,只有类Y的4个字节,共4个字节,不需要补齐,最终为4个字节;          注意:补齐就是将数值调整为某个整数的整数倍,在32位机器上,一般Alignment 为 4 个字节。          
    4. 一个虚基类对象只会在派生类中存在以分实体,不管它在继承体系中出现多少次


    以上几个例子总结类的大小:

    1. 类的非静态成员数据类型的大小之和
    2. 有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚表的指针)
    3. 为了优化存取效率,进行的边缘调整
    4. 与类中的构造函数/析构函数以及其他成员函数无关


    参考文献:

    http://www.voidcn.com/article/p-kezfjiov-om.html

  • 相关阅读:
    HUD 1284 钱币兑换问题
    HUD 1284 钱币兑换问题
    HDU 1283 最简单的计算机
    HDU 1283 最简单的计算机
    商品搜索引擎---推荐系统设计
    Spark机器学习:TF-IDF实例讲解
    【读书笔记】Elasticsearch集成Hadoop最佳实践
    Java面试题集合
    Spring Boot企业微信点餐系统-第一章-课程介绍
    Eclipse下svn的创建分支/合并/切换使用
  • 原文地址:https://www.cnblogs.com/ovs98/p/9913618.html
Copyright © 2020-2023  润新知