C++のオブジェクトの構造

5266 ワード

オブジェクトの初期化
前のコード:
#include 

class Test
{
private:
    int i;
    int j;
public:
    int getI() { return i; }
    int getJ() { return j; }
};

Test gt;

int main()
{
    printf("gt.i = %d
", gt.getI()); printf("gt.j = %d
", gt.getJ()); Test t1; printf("t1.i = %d
", t1.getI()); printf("t1.j = %d
", t1.getJ()); return 0; }

上記のコードには何が印刷されますか?私のところには直接運行結果を載せます.
gt.i = 0
gt.j = 0
t1.i = 1404627760
t1.j = 32767
  • プログラム設計の観点から、オブジェクトは変数にすぎない.
  • スタックにオブジェクトを作成するとき、メンバー変数は最初はランダム値
  • である.
  • スタックにオブジェクトを作成するとき、メンバー変数は最初はランダム値
  • である.
  • 静的記憶領域でオブジェクトを作成するとき、メンバー変数は最初は0値
  • である.

    一般的に、オブジェクトには初期状態の解決策が必要です.
    (1)       public initialize  
    (2)         initialize       
    
     class Test
     {
        private:
            int i;
            int j;
        public:
            void initialize(){i = 0; j = 0;}
            int getI()  {return i;}
            int getJ()  {return j;}
     }
    

    問題:
    (1)initializeは単なる一般関数であり,呼び出しを表示する必要がある.
    (2)initialize関数が呼び出されていない場合、実行結果は不確定
    コンストラクタ
  • C++でクラス名と同じ特殊メンバー関数を定義できます.
  • という特殊なメンバー関数を構造関数
  • と呼ぶ.
  • コンストラクション関数には、戻りタイプの宣言はありません.
  • コンストラクション関数は、オブジェクト定義時に自動的に呼び出される
  • .
  • パラメータ付きコンストラクション関数
  • 構造関数は、必要に応じてパラメータ
  • を定義することができる.
  • クラスに複数のリロードが存在することができるコンストラクタ
  • 構造関数のリロードはC++リロードの規則
  • に従う.

    例:
    class Test
    {
        public:
            Test(int v)
            {
                //use V to initialize member
            }
    }
    
  • オブジェクト定義とオブジェクト宣言が異なる
  • オブジェクト定義---オブジェクトの空間を申請し、コンストラクタ
  • を呼び出す.
  • オブジェクト宣言---コンパイラにこのようなオブジェクトが存在することを伝える
  •     Test t; //           
        
        int main()
        {
            //          t   Test   
            extern Test t;
            
            return 0;
        }
    
  • コンストラクタの自動呼び出し
  •     class Test
        {
            public:
                Test() { }
                Test(int v) { }
        };
        
        int main()
        {
            Test t;         //  Test()
            Test t1(1);     //  Test(int v)
            Test t2 = 1;    //  Test(int v)
            
            return 0;
        }
    
  • コンストラクション関数の呼び出し
  • 一般的に、コンストラクション関数は、オブジェクト定義時に
  • に自動的に呼び出される.
  • 特別な場合、構造関数
  • を手動で呼び出す必要がある.
    #include 
    
    class Test
    {
    private:
        int m_value;
    public:
        Test() 
        { 
            printf("Test()
    "); m_value = 0; } Test(int v) { printf("Test(int v), v = %d
    ", v); m_value = v; } int getValue() { return m_value; } }; int main() { // Test ta[3] = {Test(), Test(1), Test(2)}; for(int i=0; i<3; i++) { printf("ta[%d].getValue() = %d
    ", i , ta[i].getValue()); } Test t = Test(100); printf("t.getValue() = %d
    ", t.getValue()); return 0; }

    特殊な構造関数
  • の2つの特殊な構造関数
  • 無パラメトリック構造関数---パラメータのない構造関数
  • コピーコンストラクタ---パラメータはconst class_name&のコンストラクション関数
  • パラメトリック構造関数なし
  • クラスにコンストラクション関数が定義されていない場合、コンパイラはデフォルトで非パラメトリックコンストラクション関数を提供し、その関数体は空の
  • である.
  • コピーコンストラクタ
  • クラスにコピーコンストラクタが定義されていない場合、コンパイラはデフォルトで、メンバー変数の値を簡単にコピーするコピーコンストラクタを提供します.
  • #include 
    
    class Test
    {
    private:
        int i;
        int j;
    public:
        int getI()
        {
            return i;
        }
        int getJ()
        {
            return j;
        }
        //      
        /*Test(const Test& t)
         {
         i = t.i;
         j = t.j;
         }
         //    
         Test()
         {
         }*/
    };
    
    int main()
    {
        Test t1;
        Test t2 = t1;
        
        printf("t1.i = %d, t1.j = %d
    ", t1.getI(), t1.getJ()); printf("t2.i = %d, t2.j = %d
    ", t2.getI(), t2.getJ()); return 0; }

    コピーコンストラクタ
  • コピー構造関数の意義(一)
  • 互換C言語の初期化方式
  • 初期化動作は、所望の論理
  • に適合することができる.
  • コピー構造関数の意義(二)
  • 浅いコピー---コピー後のオブジェクトの物理状態は同じ
  • である.
  • 深いコピー---コピー後のオブジェクトの論理状態は同じ
  • である.
  • コンパイラが提供するコピー構造関数は、浅いコピーのみを行います.

  • 例を挙げます.
    #include 
    
    class Test
    {
    private:
        int i;
        int j;
        int* p;
    public:
        int getI()
        {
            return i;
        }
        int getJ()
        {
            return j;
        }
        int* getP()
        {
            return p;
        }
        //        ,     
        Test(const Test& t)
        {
            i = t.i;
            j = t.j;
            p = new int;
            
            *p = *t.p;
        }
        Test(int v)
        {
            i = 1;
            j = 2;
            p = new int;
            
            *p = v;
        }
        void free()
        {
            delete p;
        }
    };
    
    int main()
    {
        Test t1(3);
        Test t2(t1);
        
        printf("t1.i = %d, t1.j = %d, *t1.p = %d
    ", t1.getI(), t1.getJ(), *t1.getP()); printf("t2.i = %d, t2.j = %d, *t2.p = %d
    ", t2.getI(), t2.getJ(), *t2.getP()); t1.free(); t2.free(); return 0; }
  • 深いコピーはいつ必要ですか?
  • オブジェクトのメンバーがシステム内のリソースを指しています.
  • メンバーは、動的メモリ領域
  • を指す.
  • メンバーが外部メモリのファイルを開く
  • メンバーは、システム内のネットワークポート
  • を使用します.
  • .....


  • 一般原則
  • カスタムコピーコンストラクタは、必ず深いコピーを実現する必要があります!!!