C++ コア プログラミング クラスとオブジェクト --- オブジェクトの特性 --- コンストラクターとデストラクター (オブジェクトの初期化とクリーンアップ)

目次

1. コンストラクターとデストラクター (オブジェクトの初期化とクリーンアップ)

効果

機能実現

2. コンストラクター

構文: classname() {}

3. デストラクター

構文 ~classname(){}

4. コンストラクターの分類と呼び出し

5. コピーコンストラクタの呼び出しタイミング

6. コンストラクター呼び出し規則

7. ディープコピーとシャローコピーの問題        

8. 初期化リスト

9. クラスメンバーとしてのクラスオブジェクト

10. 静的メンバー

1. 静的メンバーの分類

2. 静的メンバー変数

3. 静的メンバー関数


1. コンストラクターとデストラクター (オブジェクトの初期化とクリーンアップ)

オブジェクトの初期化とクリーンアップ

        私たちが生活する中で、私たちが購入する電化製品には基本的に工場出荷時にカバーが取り付けられていますが、安全を確保するために、使用しない日には自分の情報やデータの一部も削除します。

        C++ のオブジェクト指向は人生から来ており、各オブジェクトには初期設定と、オブジェクトが破棄される前にデータをクリーンアップするための設定もあります。

効果

        オブジェクトまたは変数には初期状態がなく、その使用による結果は不明です。

        同様に、オブジェクトまたは変数を使用した後、時間内にクリーンアップしないと、特定のセキュリティ上の問題が発生します。

機能実現

        C++ は、コンストラクターとデストラクターを使用して、上記の問題を解決します。これら 2 つの関数は、オブジェクトの初期化とクリーンアップを完了するために、コンパイラーによって自動的に呼び出されます。オブジェクトの初期化とクリーンアップは、コンパイラーによって強制的に実行されるため、提供しない場合は、構築と破棄では、コンパイラーが提供するコンストラクターとデストラクターは空の実装です。

        

  • コンストラクター: 主にオブジェクトの作成時にオブジェクトのメンバー プロパティに値を割り当てるために使用されます。コンストラクターはコンパイラーによって自動的に呼び出され、手動で呼び出す必要はありません。
  • デストラクター: 主に、クリーンアップ作業を実行するためにオブジェクトが破棄される前にシステムを自動的に呼び出すために使用されます。

 

2. コンストラクター

構文: classname() {}

  • コンストラクターには戻り値がなく、void を書き込みません
  • 関数名はクラス名と同じです
  • コンストラクターにはパラメーターを含めることができるため、オーバーロードが発生する可能性があります
  • プログラムがオブジェクトを呼び出すと、コンストラクターが自動的に呼び出されます。手動で呼び出す必要はなく、一度だけ呼び出されます。

例:

#include<iostream>

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 构造函数

person()

    {

        // 在创建一个对象的时候会自动调用一次

        cout<<"person 构造函数的调用"<<endl;

    }

};

int main ()

{

    person p;

    return 0;

}

操作結果:

 

3. デストラクター

構文 ~classname(){}

  • デストラクターには戻り値がなく、void を書き込みません。
  • 関数名はクラス名と同じです
  • デストラクターはパラメーターを持つことができないため、オーバーロードは発生しません。
  • プログラムはオブジェクトが破棄される前に自動的にデストラクターを呼び出します。手動で呼び出す必要はなく、一度だけ呼び出されます。

例:

#include<iostream>

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 析构函数

    ~person ()

    {

        cout<<"析构函数的调用"<<endl;

    }

};

void test()

{

    person p; // 在栈上的数据,在test执行完毕后就会释放这个对象

}

int main ()

{

    person p1;  // 在main函数中也会在整个程序中执行完的时候执行

    return 0;

}

操作結果:

4. コンストラクターの分類と呼び出し

2 つの分類方法:

  •         パラメータに応じて分割: パラメータ化された構築とパラメータなしの構築
  •         通常施工とコピー施工の種類別

3 つの呼び出し方法:

  •         ブラケティング
    •         表示方法
      •         暗黙的な変換方法

例:

#include<iostream>

using namespace std;

class person {

public:

    // 构造函数

    person() // 无参

    {

        cout << "person 的无参数的构造函数调用。" << endl;

    }

    person(int a) // 有参

    {

        age = a;

        cout << "person 的有参数的构造函数调用。" << endl;

    }



    // 拷贝构造函数

    person(const person& p)

    {

        // 把另一个person的属性传入进来 +const 防止改变原来的属性

        // 将传入的人身上的所有的属性全部拷贝到自己的身上

        age = p.age;

        cout << "person 的拷贝构造函数调用。" << endl;

    }



    // 析构函数

    ~person()

    {

        cout << "person 的析构函数的调用。" << endl;

    }

    int age;

};

void test()

{

    // 调用

    // 1. 括号法

    cout << "括号法调用" << endl;

    person p1; // 默认构造函数调用

    // 注意事项1:默认构造函数的调用不要加()  因为会被认为是一个函数的声明,不会认为在创建对象

    person p2(10); // 调用有参构造函数

    // 拷贝构造函数的调用

    person p3(p2); // 拷贝构造函数的调用



    cout << "p2的年龄:" << p2.age << endl;

    cout << "p3的年龄:" << p3.age << endl;

    cout << endl;



    // 2. 显示法

    cout << "显示法调用" << endl;

    person p4; // 默认构造函数

    person p5 = person(10); // 有参构造

    person p6 = person(p3);  // 拷贝构造

    // 右侧相当于匿名对象,当执行结束后系统会立即回收掉匿名对象
    // 注意事项2:不要利用拷贝构造函数 初始化 匿名对象
    // 编译器会认为  person(p3)  === person p3,相当于时对象的声明

    cout << endl;



    cout << "测试匿名对象" << endl;

    person(10);

    cout << "aaa" << endl << endl;



    // 3. 隐式转换法

    cout << "隐式转换法调用" << endl;

    person p7 = 10; // 相当于person p3 = person(10);是一种有参构造

    person p8 = p7;

    cout << endl;

   }

int main()

{

    test();

    return 0;

    system("pause");

}

操作結果:

5. コピーコンストラクタの呼び出しタイミング

C++ でコピー コンストラクターが呼び出される場合は、通常、次の 3 つの状況が考えられます。

  • 作成済みのオブジェクトを使用して新しいオブジェクトを初期化する
  • 値渡しメソッドを使用して関数パラメータに値を渡す
  • ローカルオブジェクトを値で返します

例:

#include<iostream>
using namespace std;
// 拷贝函数的三种调用时机
class person {
public:
    person()
    {
        cout << "person 的默认函数构造" << endl;
    }
    // 有参
    person(int in_age)
    {
        age = in_age;
        cout << "有参构造函数" << endl;
    }
    // 拷贝
    person(const person& p)
    {
        age = p.age;
        cout << "拷贝构造函数" << endl;
    }
    // 析构
    ~person()
    {
        cout << "person 的析构函数的调用" << endl;
    }
//private:
    int age;
};

// 1. 使用一个已经创建完毕的对象来初始化一个新的对象
void test01()
{
    person p1(20); // 有参构造
    person p2(p1); // 拷贝构造
    cout << "p2的年龄为:" << p2.age << endl;
}

// 2.值传递的方式给函数参数传值
void do_work(person p)
{

}

void test02()
{
    person p;
    do_work(p);
}

// 3. 以值的方式返回局部对象
person do_work2()
{
    person p1;
    cout << (int*)&p1 << endl;
    return p1;  // 拷贝一个新的对象用于返回
}

void test03()
{
    person p = do_work2(); // 得到的是拷贝的返回值
    cout << (int*)&p << endl;
}

int main()
{
    test01();
    cout << endl;
    test02();
    cout << endl;
    test03();
    return 0;
}

操作結果:

6. コンストラクター呼び出し規則

  • デフォルトでは、C++ コンパイラは少なくとも 3 つの関数をクラスに追加します。
  •         1. デフォルトのコンストラクター (パラメーターなし、空の関数本体)
  •         2. デフォルトのデストラクター (パラメーターなし、空の関数本体)
  •         3. 属性の値をコピーするデフォルトのコピー コンストラクター
  • コンストラクター呼び出しルール:
  •         パラメーター化されたコンストラクターを作成した場合、コンパイラーはデフォルトのコンストラクターを提供しなくなりますが、コピー コンストラクターは引き続き提供します。
  •         コピー コンストラクターが記述された場合、コンパイラーはデフォルト コンストラクターとパラメーター化されたコンストラクターを提供しなくなります。

例:

#include<iostream>

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<<endl;

    }

 

    // 有参

    //  如果自己提供了有参构造,编译器就不再提供默认构造

    person(int in_age)

    {

        age = in_age;

        cout<<"person 的有参构造函数"<<endl;

    }

 

    // 拷贝

    // 如果自己不提供,编译器也会提供拷贝函数,值拷贝(两个析构,一个有参)

    /*person(const person& p)

    {

        this->age = p.age;

        cout<<"person 的拷贝构造函数"<<endl;

    }*/

 

    // 析构

    ~person()

    {

        cout<<"person 的析构函数的调用"<<endl;

    }

 

    int age;

};

void test01()

{

    person p;

    p.age = 18;

 

    person p1(p);

    cout<<"p1的年龄:"<<p1.age<<endl;

}

int main()

{

    test01();

    return 0;

}

操作結果:

 

7. ディープコピーとシャローコピーの問題        

  • 浅いコピー: 単純な割り当てコピー操作
  • ディープコピー: ヒープ領域にスペースを再適用し、コピー操作を実行します。

シャロー コピーによって引き起こされる問題は、ヒープ領域のメモリが繰り返し解放されることですが、ディープ コピーを使用することで解決できます。

例:

#include<iostream>

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<<endl;

    }

 

    // 有参

    person(int age, double height)

    {

        this->age = age;

        cout<<"person 的有参构造函数"<<endl; 

        this->height = new double(height); // new 一个新的指针变量,创建在堆区

    }

 

    // 拷贝

    person(const person & p)

    {

        this->age = p.age;  // 编译器默认实现

        cout<<"person 的拷贝构造函数"<<endl;

        // this->age = p.height; // 编译器的 拷贝构造默认实现方式

 

        // 解决浅拷贝带来的问题

 

        // 两个对象的指针分别设立在不同的存储地址

        this->height = new double(*p.height); // 利用深拷贝来解决浅拷贝的问题

    }

 

    // 析构

    ~person()

    {

        // 将我们在堆区开辟的数据做释放操作

        if(height !=NULL){

            delete height;

            height = NULL;

        }

        cout<<"person 的析构函数的调用"<<endl;

    }

   

    int age;

    double * height; // 身高数据的地址

 

    // 编译器提供的拷贝构造函数 会做浅拷贝处理  会重复释放

    // 身高是定义在堆区的,指针类型的存储的是地址,不能重复释放

};

void test01()

{

    person p1(18,1.80);

    cout<<"p1的年龄为: "<<p1.age<<endl;

    cout<<"p1的身高为:"<<*p1.height<<endl;

 

    person p2(p1);

    cout<<"p2的年龄为:"<<p2.age<<endl;

    cout<<"p2的身高为:"<<*p2.height<<endl;

}

int main()

{

    test01();

    return 0;

}

操作結果:

 

8. 初期化リスト

機能: C++ は、プロパティを初期化するための初期化リスト構文を提供します。

構文: Constructor(): プロパティ 1 (値 1)、プロパティ 2 (値 2)... {}

例:

#include<iostream>

using namespace std;

class person

{

public:

    // 传统初始化操作

    /*person(int a ,int b,int c)

    {

        A = a;

        B = b;

        C = c;

    }*/

 

    // 初始化列表属性,赋值默认值

    /*person():A(10),B(20),C(30)

    {

 

    }*/

 

    // 设置成变量

    person(int a,int b,int c):A(a),B(b),C(c)

    {

 

    }

 

    int A;

    int B;

    int C;

};

void test01()

{

    // 每次调用一种

    person p(10,20,30);

    cout<<"A = "<<p.A<<endl;

    cout<<"B = "<<p.B<<endl;

    cout<<"C = "<<p.C<<endl;

 

}

int main()

{

    test01();

    return 0;

}

操作結果: 

9.クラスメンバーとしてのクラスオブジェクト

C++ クラスのメンバーは、別のクラスのオブジェクトであり、オブジェクト メンバーと呼ばれます。

例えば:

class A{};

class B

{

A a;

};

クラス B にはオブジェクト A がメンバーとして含まれており、A はオブジェクトのメンバーです

次に、B オブジェクトを作成するとき、A と B の構築と破壊の順序は ABBA になります。

例:

#include<iostream>

#include<string>

using namespace std;

// 设计一个手机类

class phone{

public:

    // 给手机命名

    // 构造函数

    phone(string in_name)

    {

        p_name = in_name;

        cout<<"这是phone的构造函数调用"<<endl;

    }

    ~phone()

    {

            cout<<"phone 析构函数的调用"<<endl;

    }

 

    // 手机类型的设计

    string p_name;

};

// 设计一个人的类

class person

{

public:

    // 获取内容

    person(string name,string p_name):my_name(name),my_phone(p_name)

    {

        // 相当于下边这样,隐式转换法

        // phone my_phone = p_name;

        cout<<"这是person 的构造函数调用"<<endl;

    }

    ~person()

    {

            cout<<"person 析构函数的调用"<<endl;

    }

 

// 姓名

     string my_name;

    // 手机

    phone my_phone;

};

// 当其他类作为本类的成员,构造时先构造其他类的对象,再构造自身

//  析构时先析构自身,再析构其他的对象

void test01()

{

    person p("张三","苹果MAX");

    cout<<p.my_name<<"有一个"<<p.my_phone.p_name<<"手机"<<endl;

}

int main()

{

    test01();

    return 0;

}

操作結果:

10. 静的メンバー

        静的メンバーは、メンバー変数およびメンバー関数の前にキーワード static を追加することによって静的メンバーと呼ばれます。

1. 静的メンバーの分類

静的メンバー変数:    

  • すべてのオブジェクトが同じデータを共有する
  • コンパイル段階でメモリを割り当てる
  • クラス内で宣言、クラス外で初期化

静的メンバー関数:

  • すべてのオブジェクトは同じ機能を共有します
  • 静的メンバー関数は静的メンバー変数にのみアクセスできます

2. 静的メンバー変数

例:

#include<iostream>

#include<string>

using namespace std;

class person {

public:

    static int m_A; // 静态成员变量

    // 类内声明,类外初始化



private:

    static int m_B;// 静态成员变量

    // 在类外访问不到

};

int person::m_A = 0;

int person::m_B = 200;



// 两种访问方式

void test01()

{

    // 1. 通过对象访问

    cout << "通过对象访问" << endl;

    person p1;

    cout << "p1_a = " << p1.m_A << endl;



    person p2;
    
    p2.m_A = 200;

    cout << "共享数据,p2修改了数据,p1访问就变为p2修改后内容" << endl;

    cout << "p1_a = " << p1.m_A << endl;

    cout << "p2_a = " << p2.m_A << endl;



    // 2. 通过类名访问

    cout << "通过类名访问:" << person::m_A << endl;



    //并且私有权限类外访问不到

}

int main()

{

    test01();



    return 0;

}

操作結果:

  

3. 静的メンバー関数

例:

#include<iostream>

#include<string>

using namespace std;

class person{

public:

    // 静态的成员函数

    static void func()

    {

        m_A = 100;

        // m_B = 200;  // 静态成员函数不能访问非静态成员变量

        // 静态成员函数是每个对象都共享的,调用非静态成员变量时不知道修改的是哪个对象的变量

        cout<<"静态函数的调用"<<endl;

    }

    static int m_A; // 静态成员变量

    // 类内声明,类外初始化

    int m_B;

private:

    static  void  func2()

    {

        cout<<"这是private下的调用"<<endl;
    }

};

int person::m_A = 0;

 

// 两种访问方式

void test01()

{

    // 1. 通过对象访问

    cout << "通过对象访问" << endl;

    person p1;

    p1.func();

 

    // 2. 通过类名访问

    cout<<"通过类名访问:";

    person::func();

 

    person::func2();// 类外访问不到私有静态成员函数

}

int main()

{

    test01();
    return 0;

}

操作結果: 

 

おすすめ

転載: blog.csdn.net/hjl011006/article/details/133550565