天天看點

構造函數與析構函數執行順序及其與虛函數關系

1.構造函數與析構函數執行順序:

(1) 繼承類首先從基類構造函數開始執行,然後調用成員對象的構造函數,最後調用自己的構造函數(中間可能存在多個層次),;

(2)析構函數的執行順序與構造函數相反,析構函數調用之後會調用成員對象的析構函數;

(3)成員對象的構造順序是聲明順序,不是構造函數的初始化清單順序,這樣可以保證順序的唯一性。

(4) 局部對象在程式結束或函數退出時會自動調用析構函數,但是配置設定在堆中的對象(new)不會,除非顯示調用delete。

借用代碼說明:

#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

using namespace::std;

class C
{
        public:
                C() {cout<<"C";}
                ~C(){cout<<"~C";}
};

class A
{
        public:
                A() {cout<<"A";}
                ~A(){cout<<"~A";}
};


class B : public A
{
        public:
                //B(A &a, C &c):_a(a), _c(c) {cout<<"B";}
                B() {cout<<"B";}
                ~B(){cout<<"~B";}
        private:
                C _c;
                A _a;
};

int main(int argc, char**argv)
{
        //C *d=new C();
        //A *e=new A();
        C c;
        A a;
        //B b(a, c);
        //B b(*d, *e);
        B b;

        return 0;
}
           

輸出結果為:CAACAB~B~A~C~A~A~C

構造c和a輸出CA,B從基類A開始構造輸出A,然後是其成員對象_c和_a按照聲明順序構造CA(如果調整聲明順序會發現輸出順序相反),然後調用B自身的構造函數。

main函數退出,析構局部對象,從B開始首先B的析構函數~B,然後成員對象_c和_a,析構順序與聲明順序相反~A~C(如果調整聲明順序會發現輸出順序相反),然後B的基類調用析構函數~A,然後a和c分别調用析構函數~A~C。

如果main函數使用對象d和e(注釋a和c),因為是配置設定在堆中,是以函數退出時候不會釋放d和e,即不會調用析構函數,輸出結果變為CAACAB~B~A~C~A。

同時,如果B的構造函數采用注釋行的方式(main函數中b使用B b(a, c)),對象_a和_c的指派方式是采用拷貝函數,不會調用構造函數,是以會少輸出CA一次,輸出結果為CAAB~B~C~A~A~A~C。但是在對象已經建立,是以在析構的時候,析構函數照常調用。

2. 虛函數與構造函數和析構函數 

(1) 構造函數本身不能是虛拟函數;并且虛機制在構造函數中不起作用(在構造函數中的虛拟函數隻會調用它的本地版本)。

(2) 析構函數本身常常要求是虛拟函數;但虛機制在析構函數中不起作用,因為可能會引起調用已經被delete掉的類的虛拟函數的問題。

(3) 若類中使用了虛拟函數,析構函數一定要是虛拟函數,比如使用虛拟機制調用delete,沒有虛拟的析構函數,不能保證delete的是你希望delete的對象。 代碼說明:

#include <iostream>
#include <stdio.h>

using namespace::std;

class Base
{
        public:
                Base()
                {
                        init();
                }

                virtual void init()
                {
                        printf("init base\n");
                }
};

class Super : public Base
{
        public:
                Super()
                {
                        init();
                }

                virtual void init()
                {
                        printf("init super\n");
                }
};

int main(int argc, char**argv)
{
        Base *ptr=new Super();
        return 0;
}
           

輸出結果為: init base

init super

結果無需繼續解釋。

如有錯誤,謝謝指出更正!

繼續閱讀