受講ノート---プログラム設計とアルゴリズム(三)C++対象向けプログラム設計(郭偉先生)---第二週

19252 ワード

Week 2
Contents
メンバーと非表示
コンストラクタ
こうぞうかんすう
Thoughts
メンバーと非表示
メンバー関数の定義と呼び出し
  • メンバー関数はクラス内部に定義することができ、通常の関数と同様にクラス外部に定義することもできるが、その宣言は常にクラス内部にあり、形式は以下の
  • である.
    class A {
        int Fun();
    }
    
    int A::Fun()
    {
        ...
    }
  • メンバー関数は、オブジェクトまたはオブジェクトへのポインタによって呼び出される必要があります.private and public
  • キーワードは定義時に繰り返し使用することができ、順序
  • に限らない.
    class A {
    public:
        ...
    private:
        ...
    public:
        ...
    }
  • キーワードのないものはprivate
    class A {
    int a; // private
    int Fun(); //private
    public:
    int b; // public
    };
  • とみなす.
  • privateメンバーはメンバー関数内でのみアクセス可能であり、publicメンバーはどこでも
  • にアクセス可能である.
    メンバー関数のアクセス可能範囲
  • 現在のオブジェクトのすべてのプロパティ、関数
  • 同類オブジェクトのすべての属性および関数(関数パラメータで伝達される)
  • 残りの地帯のアクセス範囲
    オブジェクトの共通メンバー
    「非表示」メカニズムとその役割
  • プライベートメンバーを設定するメカニズムは「非表示」
  • です.
  • メンバーへのアクセスを強制するには、必ずメンバー関数を使用します.その後、データ型、戻り値型などのメンバーの属性を変更した後、対応するメンバー関数の操作を変更するだけで、残りのゾーンを変更することなく、そのメンバーの文
  • を直接使用します.
    メカニズムインスタンスの非表示
    #include
    #include
    
    class CEmployee {
    private:
        char szName[30];
        int getSalary();
    public:
        int salary;
        void setName(const char * name);
        void getName(char * name);
        void averageSalary(CEmployee e1, CEmployee e2);
    };
    
    void CEmployee::setName(const char * name)
    {
                strcpy(szName, name);   //        szName,  
    }
    
    void CEmployee::getName(char * name)
    {
                strcpy(name, szName);//        szName,  
    }
    
    int CEmployee::getSalary()
    {
                return salary;
    }
    
    int main()
    {
        CEmployee A;
        char name[30];
        A.setName("Tom"); //   
        A.getSalary(); //   ,             
        strcpy(name, A.szName); //   ,        
        return 0;
    }
    

    メンバー関数のリロードおよびパラメータのデフォルト
    メンバー関数は、パラメータのデフォルトとリロードもできます.
    しかし、二義的な誤りは避けなければならない.
    class A {
        void Fun();
        void Fun(int a = 0);
    }
    
    int main()
    {
        Fun();// ambiguous
    }

    コンストラクタ
    コンストラクタの基本概念
  • メンバー関数の
  • 名前はクラス名と同じで、パラメータがあってもよいし、戻り値があってもよい(void)
  • は初期化を担当し、空間
  • の割り当てを担当しない.
  • プログラムは自動的にパラメータなし構造関数を生成し、何の操作もありません.プログラマが自分でコンストラクション関数を定義した後、デフォルトのコンストラクション関数は失効し、パラメータなしコンストラクション関数が必要な場合は
  • を自分で書く必要があります.
  • クラスには複数のコンストラクション関数があり、各関数間はリロード関係
  • である.
  • オブジェクトが生成すると、コンストラクション関数が呼び出され(どのような形式で生成するかにかかわらず)、コンストラクション関数が実行され、
  • が1回のみ実行する.
  • は、A::A(A)式の構造関数
  • の出現を許さない.
    コンストラクション関数の意味
  • 自動初期化オブジェクト、追加の初期化オブジェクト
  • は不要
  • 初期化による不明なエラーを回避する
  • privateコンストラクタの許可privateコンストラクション関数は、オブジェクトを直接初期化するために使用できません.
    コンストラクション関数の使用例
    コンストラクション関数の基本的な使用
    #include
    
    class Complex {
        double real, imag;  // private
    public:
        Complex(double real, double imag);
        Complex(double i);
        Complex(Complex, Complex);
    };
    
    Complex::Complex(double r, double i)
    {
        real = r;
        imag = i;
    }
    
    Complex::Complex(double i)
    {
        real = i;
        imag = 0;
    }
    
    Complex::Complex(Complex c1, Complex c2)
    {
        real = c1.real + c2.real;   
        imag = c1.imag + c2.imag;   //    ,          
    }
    
    int main()
    {
        Complex a;      //   ,           
        Complex c1(4), c2(2, 1); //       double
        Complex c(c1, c2);
        Complex * p = new Complex(1);
    
        return 0;
    }

    コンストラクション関数と配列
    #include
    
    class Test {
    public:
        int x;
        Test() 
        {
            std::cout << "Constructor 1 Called." << std::endl;
        }
        Test(int i)
        {
            x = i;
            std::cout << "Constructor 2 Called." << std::endl;
        }
        Test(int a, int b)
        {
            x = a + b;
            std::cout << "Constructor 3 Called." << std::endl;
        }
    };
    
    
    int main()
    {
        Test A[2] = {2};        // A[1]           constructor
        std::cout << "Step 1" << std::endl;
        Test B[2] = {Test(2, 1), 5};
        std::cout << "Step 2" << std::endl;
        Test * P = new Test[2]{1, Test(2,1)};   //        C++11  ,warning:       ,new feature
        std::cout << "Step 3" << std::endl;
        delete []P;
        Test * PA[3] = {new Test(1), new Test(1,2), NULL};  // PA[2]         constructor
        delete PA[1];
        delete PA[0];
        std::cout << "Step 4" << std::endl;
    
        return 0;
    }

    しゅつりょく
    Constructor 2 Called. Constructor 1 Called. Step 1 Constructor 3 Called. Constructor 2 Called. Step 2 Constructor 2 Called. Constructor 3 Called. Step 3 Constructor 2 Called. Constructor 3 Called. Step 4
    コンストラクタのコピー
    構造関数の基本概念のコピー
  • 構造関数の1つ
  • には、同類オブジェクトへの参照(constまたは非const、一般にconstを用いる)
  • というパラメータが1つしかない.
  • コンパイラはレプリケーションコンストラクタを自動的に生成し、プログラマが自分で定義すると、自動的に生成されるレプリケーションコンストラクタは
  • は存在しない.
    コンストラクタインスタンスのコピー
    自動生成されたレプリケーションコンストラクタ
    #include
    
    class A {
    public:
        int v;
        A(int i)
        {
            v = i;
        }
    };
    
    int main()
    {
        A c1(5);
        A c2(c1);   //             
    
        std::cout << c1.v << std::endl;
        std::cout << c2.v << std::endl;
    
        return 0;
    }

    しゅつりょく
    5 5
    3つのレプリケーション構造関数が機能する場合
  • 別のクラスのオブジェクトを1つのオブジェクトで初期化する
    
    #include
    
    
    class A {
    public:
    int v;
    A(int i)
    {
        v = i;
    }
    A(A & a)
    {
        v = a.v;
        std::cout << "Copy constructor called." << std::endl;
    }
    };
    
    int main()
    {
    A c1(5);
    A c2(c1);   //    ,         
    A c3 = c2;  //         ,      ,         
    
    c1 = c2;    //     ,         
    return 0;
    }
    Output
    Copy constructor called. Copy constructor called.
  • 関数パラメータはクラスのオブジェクトであり、パラメータはレプリケーションコンストラクタによって
    
    #include
    
    
    class A {
    public:
    int v;
    A(int i)
    {
        v = i;
    }
    A(A & a)
    {
        v = a.v;
        std::cout << "Copy constructor called." << std::endl;
    }
    };
    
    void Fun(A a)
    {
    std::cout << a.v << std::endl;
    }
    
    int main()
    {
    A c1(5);
    
    Fun(c1);    //              
    return 0;
    }
    Output初期化される
    Copy constructor called. 5
  • 関数戻り値がクラスのオブジェクトであり、戻り時にコピーコンストラクタ初期化戻り値
    
    #include
    
    
    class A {
    public:
    int v;
    A(int i)
    {
        v = i;
    }
    A(A & a)
    {
        v = a.v;
        std::cout << "Copy constructor called." << std::endl;
    }
    };
    
    A Fun(A a)
    {
    std::cout << a.v << std::endl;
    return a;
    }
    
    int main()
    {
    A c1(5);
    
    std::cout << Fun(c1).v << std::endl;    //              ,         
    return 0;
    }
    Output
    Copy constructor called. 5 Copy constructor called. 5

  • Tips
  • 賦値文レプリケーションコンストラクタ
  • を呼び出さない
  • 定数参照パラメータを使用して、コンストラクション関数の呼び出しオーバーヘッドを複製することを回避し、関数内で参照されるオブジェクトを変更することを回避する
  • .
    タイプ変換コンストラクタ
    タイプ変換構造関数の基本概念
  • 実装タイプの自動変換を目的とする
  • には1つのパラメータしかなく、複製構造関数(すなわち、このパラメータは& A型ではない)ではなく、タイプ変換構造関数
  • と呼ぶことができる.
  • 一時変数
  • を構築する.
    タイプ変換コンストラクタの例
    #include
    
    class Complex {
    public:
        double real, imag;
        Complex(int i)  // convert int into Complex
        {
            std::cout << "IntConstructor Called." << std::endl;
            real = i;
            imag = 0;
        }
        Complex(double r, double i)
        {
            real = r;
            imag = i;
        }
    };
    
    int main()
    {
        Complex aa(4,5);
        Complex a(4);   //            ,     a,       
    
        Complex aaa = 4;    //        ,       
    
        aaa = 9;        //            ,   Complex(9)        ,   aaa
    
        return 0;
    }

    Output:
    IntConstructor Called. IntConstructor Called. IntConstructor Called.
    こうぞうかんすう
    構造関数の基本概念
  • 名前はクラス名と同じで、クラス名の前に~記号
  • を付ける.
  • パラメータおよび戻り値なし
  • クラスには、最大1つの構造関数
  • があります.
  • 構造関数は、オブジェクトが消滅したときに呼び出される
  • デフォルトの解析関数はほとんど操作されず、プログラマがカスタマイズするとデフォルトの解析関数は
  • から消えます.
    構造関数と配列、ポインタ
    オブジェクト配列が消滅すると、各オブジェクトの構造関数が呼び出されます.
    ポインタが指す動的に割り当てられたオブジェクトはdeleteを使用しないと消滅せず、構造関数も呼び出されません.
    構造関数と関数の戻り値
    オブジェクトが戻り値で返されると、割り当て構造関数を呼び出して一時変数を生成し、その後、解析関数を呼び出し、その後、一時変数も消滅し、解析関数も呼び出されます.
    構造関数の例
    構造関数と配列
    #include
    
    class A {
        public:
            ~A()
            {
                std::cout << "Destructor Called." << std::endl;
            }
    };
    
    int main()
    {
        A Test[2];  //     ,           
        A * P = new A[3];   //    ,  delete        
    
        return 0;
    }

    Output:
    Destructor Called. Destructor Called.
    構造関数と関数呼び出し
    #include
    
    class A {
        public:
            int x;
            A(int i) 
            {
                x = i;
            }
            A(A & a)
            {
                x = a.x;
                std::cout << "Copy called" << std::endl;
            }
            ~A()
            {
                std::cout << x << "Destructor Called" << std::endl;
            }
    };
    
    A Fun()
    {
        A b(4);
        return b;
    }
    
    A a(5);
    
    int main()
    {
        a = Fun();  //
    
        std::cout << "main ends" << std::endl;
        return 0;
    }

    Output:
    Copy called 4 Destructor Called//b 4 Destructor Called//一時変数main ends
    4Destructor Called//a
    *コンパイラでの最適化
    多くのコンパイラでは、関数のクラスのオブジェクトタイプの戻り値は、レプリケーションコンストラクション関数を呼び出して一時変数を生成したり、DebugとReleaseの2つのモードでVSが異なるなどの構造関数を呼び出したりしません.