專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

C++中類的內(nèi)存空間大。╯izeof)分析

作者:龔平   來(lái)源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年03月13日   【字體:

    在C語(yǔ)言中存在關(guān)于結(jié)構(gòu)體的存儲(chǔ)空間大小是比較深入的話題,其中涉及計(jì)算機(jī)的基本原理、操作系統(tǒng)等。我認(rèn)為對(duì)齊是C語(yǔ)言中讓很多初學(xué)者都拿不準(zhǔn)摸不透的問(wèn)題,特別是在跨平臺(tái)的情況下,對(duì)齊這種問(wèn)題更加的復(fù)雜多變,每一種系統(tǒng)都有自己獨(dú)特的對(duì)齊方式,在Windows中經(jīng)常是以結(jié)構(gòu)體重最大內(nèi)置類型的存儲(chǔ)單元的字節(jié)數(shù)作為對(duì)齊的基準(zhǔn),而在Linux中,所有的對(duì)齊都是以4個(gè)字節(jié)對(duì)齊。
 
    那么在C++中的類的內(nèi)存空間大小又有哪些特殊的問(wèn)題呢?
    首先,我認(rèn)為對(duì)齊肯定也是其中的問(wèn)題之一,對(duì)齊主要是為了加快讀取的速度。
    關(guān)于對(duì)齊這個(gè)我認(rèn)為基本上已經(jīng)是操作系統(tǒng)內(nèi)定好的,既然Linux與Windows存在差別,那么在C++的類中,關(guān)于對(duì)齊肯定也會(huì)存在一定的差別。關(guān)于對(duì)齊我認(rèn)為只要記住平時(shí)使用的系統(tǒng)的對(duì)齊準(zhǔn)則就可以了,即:在Windows中經(jīng)常是以結(jié)構(gòu)體重最大內(nèi)置類型的存儲(chǔ)單元的字節(jié)數(shù)作為對(duì)齊的基準(zhǔn),而在Linux中,所有的對(duì)齊都是以4個(gè)字節(jié)對(duì)齊。
  
    其次,我認(rèn)為就應(yīng)該討論在基類中哪些成員占有存儲(chǔ)空間,那些成員不占用內(nèi)存空間?
    在C++中占存儲(chǔ)區(qū)間的主要是非static的數(shù)據(jù)對(duì)象,主要包括各種內(nèi)置的數(shù)據(jù)類型,類對(duì)象等,類中的函數(shù)聲明以及函數(shù)定義都不算內(nèi)存空間。但是需要注意所有的virtual函數(shù)(虛函數(shù))共享一段內(nèi)存區(qū)域,一般來(lái)說(shuō)是4個(gè)字節(jié)。為什么只是包含非static數(shù)據(jù)對(duì)象呢?因?yàn)閟tatic數(shù)據(jù)并不屬于類的任何一個(gè)對(duì)象,它是類的屬性,而不是具體某一個(gè)對(duì)象的屬性,在整個(gè)內(nèi)存區(qū)域中只有一個(gè)內(nèi)存區(qū)域存儲(chǔ)對(duì)應(yīng)的static數(shù)據(jù),也就是所有的類對(duì)象共享這個(gè)數(shù)據(jù),所以不能算做具體某一個(gè)對(duì)象或者類型的內(nèi)存空間。
    因此可以認(rèn)為基類對(duì)象的存儲(chǔ)空間大小為:
    非static數(shù)據(jù)成員的大小 + 4 個(gè)字節(jié)(虛函數(shù)的存儲(chǔ)空間)
    當(dāng)然這個(gè)大小不是所有數(shù)據(jù)成員大小的疊加,而是存在一個(gè)對(duì)齊問(wèn)題,具體的應(yīng)該參考相關(guān)的對(duì)齊文章。

 
    最后,我認(rèn)為肯定要關(guān)心一下派生類的存儲(chǔ)空間了?
    在C++中,繼承類是一個(gè)比較有用的類,繼承使得各種類在基類的基礎(chǔ)上擴(kuò)展,這時(shí)候派生類中包含了基類的信息,一般而言,在基類中存在虛函數(shù)時(shí),派生類中繼承了基類的虛函數(shù),因此派生類中已經(jīng)繼承了派生類的虛函數(shù)。因此繼承類中不能再添加虛函數(shù)的存儲(chǔ)空間(因?yàn)樗械奶摵瘮?shù)共享一塊內(nèi)存區(qū)域),而僅僅需要考慮派生類中心添加進(jìn)來(lái)的非static數(shù)據(jù)成員的內(nèi)存空間大小。
    因此可以認(rèn)為派生類對(duì)象的存儲(chǔ)空間大小為:
    基類存儲(chǔ)空間 + 派生類特有的非static數(shù)據(jù)成員的存儲(chǔ)空間
 
   還有一類是比較特殊的情況,如果是虛繼承的情況下,這時(shí)的存儲(chǔ)空間大小就會(huì)發(fā)生變化。
    基類的存儲(chǔ)空間 + 派生類特有的非static數(shù)據(jù)成員的存儲(chǔ)空間 + 每一個(gè)類的虛函數(shù)存儲(chǔ)空間。
  
    下面我采用一些例子說(shuō)明上面的問(wèn)題:
    對(duì)齊的問(wèn)題:

    class test
    {
    public:
            test();
    private:
            int a;
            char c;
    };

    cout << sizeof(test) << endl;

  上面的代碼在linux以及windows下都會(huì)輸出8,而不是輸出5,這個(gè)是在C語(yǔ)言中已經(jīng)討論過(guò)的話題,但是說(shuō)明對(duì)齊在C++中也是要考慮的。關(guān)于操作系統(tǒng)的差異在后面用一個(gè)統(tǒng)一的例子說(shuō)明。
    虛函數(shù)問(wèn)題
    為了討論虛函數(shù),我們?cè)趖est類中添加一個(gè)虛析構(gòu)函數(shù),然后再測(cè)試結(jié)果。  

    class test
    {
    public:
            test();
            virtual ~test();
    private:
            int a;
            char c;
    };
    cout << sizeof(test) << endl;

這段代碼與前面的代碼沒(méi)有什么區(qū)別,只是添加了一個(gè)虛函數(shù),然后編譯調(diào)試,這時(shí)候輸出的結(jié)果12,也就是說(shuō)增加了一個(gè)虛函數(shù)以后,類的數(shù)據(jù)成員增加了4個(gè)字節(jié),那么是否是每一個(gè)虛函數(shù)都占有4個(gè)字節(jié)呢?其實(shí)是不會(huì)的,在test中加入一個(gè)新的虛函數(shù)virtual void get_a_c(),這時(shí)在輸出的結(jié)果還是12,這說(shuō)明所有的虛函數(shù)共享4個(gè)字節(jié)。
    static數(shù)據(jù)
    我們知道static數(shù)據(jù)是非對(duì)象的屬性,而是類的屬性,他不能算是某一個(gè)對(duì)象或者類型的存儲(chǔ)空間,在類定義中只能聲明,初始化只能在類外執(zhí)行,當(dāng)然有例外的。這也不做分析了。具體參看后面的大例子。
    派生類的存儲(chǔ)空間
    派生類從基類中繼承了很多成員,自己也會(huì)增加很多的成員,由于虛函數(shù)也會(huì)被繼承下來(lái),所以就是在派生類中不顯式定義虛函數(shù),在派生類中也會(huì)存在從基類繼承下來(lái)的虛函數(shù),因此虛函數(shù)不需要額外計(jì)算內(nèi)存空間,而只需要增加基類的非static成員數(shù)據(jù)大小。定義如下面所示,該函數(shù)會(huì)輸出20,剛好是添加的非static數(shù)據(jù)double d的存儲(chǔ)空間大小。證明了上面的分析。

    class test
    {
    public:
            test();
            virtual ~test();
            virtual void get_a_c();
    private:
            int a;
            char c;
    };

    class derived_test:public test
    {
    public:
            virtual ~derived_test();
    private:
            double d ;
    };

    cout << sizeof(derived_test) << endl;

測(cè)試虛繼承的類的大。

    class A
    {
            char i[3];
    public:
            virtual void a(){};
    };

    class B : public virtual A
    {
            char j[3];
    public:
            virtual void b(){}
    };

    class C: public virtual B
    {
            char k[3];
    public:
            virtual void c(){}
    };

    int main()
    {
        cout << "sizeof(A): " << sizeof(A) << endl;
        cout << "sizeof(B): " << sizeof(B) << endl;
        cout << "sizeof(C): " << sizeof(C) << endl;
        return 0;
    }

下面采用一個(gè)比較綜合的例子說(shuō)明一下操作系統(tǒng)以及各種綜合的影響分析。

    #include <iostream>
    #include <string>
    #include <vector>

    class test
    {
    public:
        test();
        virtual ~test();
        virtual void get_a_c();
    private:
        int a;
        char c;
    };

    class derived_test:public test
    {
    public:
        virtual ~derived_test();
    private:
        double d ;
    };

    class base
    {
    private:
        char a;
        static int refrence_count;
        std::string name;
        double price;
        std::vector<double> dvec;
    public:
        base();
        virtual ~base();
        static int get_count();
    };

    int base::get_count()
    {
        return refrence_count;
    }

    int base::refrence_count = 0;

    class new_base
    {
    private:
        char a;
        double price;
        std::vector<double> dvec;
        std::string name;
        static int refrence_count;
    public:
        new_base();
        virtual ~new_base();
        static int get_count();
    };

    int new_base::get_count()
    {
        return refrence_count;
    }
    int new_base::refrence_count = 0;

    class derived: public base
    {
    private:
        int min_qty;
        double discount;
        static int newp;
    public:
        derived();
        virtual ~derived(){};
    };

    class new_derived:public new_base
    {
    private:
        double discount;
        int min_pty;
        static int newp;
    public:
        new_derived();
        virtual ~new_derived(){}
    };

    int main()
    {
        std::cout << "The size of test is " << sizeof(test) << std::endl;
        std::cout << "The size of derived_test is " << sizeof(derived_test) << std::endl;
        std::cout << "The size of base is " << sizeof(base) << std::endl;
        std::cout << "The size of new_base is " << sizeof(new_base) << std::endl;
        std::cout << "The size of derived is " << sizeof(derived) << std::endl;
        std::cout << "The size of new_derived is " << sizeof(new_derived) << std::endl;
       
        return 0;
    }

上面在windows和linux的結(jié)果分別如下:
windows:

Linux:

從上面的結(jié)果可以之知道在兩個(gè)系統(tǒng)下,結(jié)果是不一樣的。說(shuō)明操作系統(tǒng)也對(duì)類的存儲(chǔ)空間大小有較大的影響。

關(guān)閉窗口

相關(guān)文章