[C++] 構築と破壊

[C++] 構築と破壊

1. デフォルトのメンバー関数

class test
{};

クラスにメンバーがまったく存在しない場合、そのクラスは空のクラスと呼ばれます。

ただし、コンパイラは空のクラスに対して 6 つのデフォルトのメンバー関数も生成します。

画像-20230204152717703

2. コンストラクター

2.1 コンストラクターの概念

コンストラクターは、クラスと同じ名前を持つ特別なメンバー関数です。

各データ メンバーに適切な初期値が設定されていることを確認するために、クラス型オブジェクトの作成時にコンパイラによって自動的に呼び出され、オブジェクトのライフ サイクル全体で 1 回だけ呼び出されます。

クラス オブジェクトが作成されると、コンパイル システム オブジェクトによってメモリ領域が割り当てられ、自動的にコンストラクターが呼び出され、メンバーの初期化が完了します。

コンストラクターの役割: オブジェクトのデータ メンバーを初期化します。

2.2 コンストラクターの機能

  1. 名前はクラス名と同じで、パラメータを持つことができますが、戻り値 (void さえも) を持つことはできません。
  2. コンストラクターはオブジェクトがインスタンス化されるときに自動的に実行されるため、手動で呼び出す必要はありません。
  3. メンバ変数に値を代入するなど、オブジェクトを初期化する機能です。
  4. クラスを定義するときにコンストラクターが記述されていない場合、システムはデフォルトの引数なしのコンストラクターを生成します。このコンストラクターにはパラメーターがなく、何も処理されません。
  5. コンストラクターが定義されている場合、システムはデフォルトの引数なしのコンストラクターを生成しなくなりました。
  6. コンストラクターはオブジェクトの生成時に自動的に呼び出されます。オブジェクトが生成されると、そのオブジェクトに対してコンストラクターを再度実行することはできません。
  7. クラスには複数のコンストラクターを含めることができ、これはオーバーロード関係となります。

2.3 コンストラクターの分類

2.3.1 デフォルトの構造

引数のないコンストラクターと完全なデフォルト コンストラクターはデフォルト コンストラクターと呼ばれ、デフォルト コンストラクターは 1 つだけです

注: 引数のないコンストラクター、完全なデフォルトのコンストラクター、およびデフォルトでコンパイラーによって生成されるように記述されていないコンストラクターは、すべてデフォルトのコンストラクターと見なすことができます。

#include<iostream>

using namespace std;

class build
{
public:
		// 无参构造函数
		// 如果创建一个类你没有写任何构造函数,则系统自动生成默认的构造函数,函数为空
		// 如果自己显示定义了一个构造函数,则不会调用系统的构造函数
	build()
	{}
};

2.3.2 パラメータを使用した構造体

一般的な構造は 2 つあります。

  1. リストの初期化
  2. 内部割り当て

この 2 つを混合することはできません

#include<iostream>

using namespace std;

class build
{
public:
		//一般构造函数
		//可以采用两种方式进行对类内成员的赋值,一种是列表初始化,而另一种是内部赋值,但二者不能同时存在
	build(int a,int b)
	{
		_x = a;
		_y = b;
	}//内部赋值

	build(int a,int b):_x(a),_y(b)
	{}//列表初始化

private:
	int _x;
	int _y;
};

2.4 コピーコンストラクター

2.4.1 コンセプト

コンストラクターをコピーします。

仮パラメータは 1 つだけあり、これはこのクラス型のオブジェクトへの参照 (通常は const 装飾) であり、既存のクラス型オブジェクトを使用して新しいオブジェクトを作成するときにコンパイラによって自動的に呼び出されます。

  • オブジェクト間でコピーするときにコピー コンストラクターを自動的に呼び出す特別なコンストラクター。
  • クラスがコピー コンストラクターを明示的に定義していない場合、システムはデフォルトのコピー コンストラクターを自動的に生成します。
  • いつ使用するか: 古いオブジェクトが新しいオブジェクトを初期化する

2.4.2 特徴

コピー コンストラクターは、コンストラクターのオーバーロードされた形式です。

コピー コンストラクターのパラメーターは 1 つだけで、クラス型オブジェクトへの参照である必要があります。値渡しのメソッドが使用されると、無限の再帰呼び出しが発生するため、コンパイラーは直接エラーを報告します。

class Date
{
public:
 	Date(int year = 1900, int month = 1, int day = 1)
 	{
		_year = year;
 		_month = month;
 		_day = day;
 	}
 // Date(const Date d)   // 错误写法:编译报错,会引发无穷递归
    Date(const Date& d)   //正确写法
	{
		_year = d._year;
 		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	return 0;
}

明示的に定義されていない場合、コンパイラはデフォルトのコピー コンストラクターを生成します。

デフォルトのコピー コンストラクター オブジェクトは、メモリ ストレージに従ってバイト オーダーでコピーされます。この種のコピーは、シャロー コピー(または値コピー)と呼ばれます。

知らせ:

コンパイラによって生成されるデフォルトのコピー コンストラクターでは、組み込み型はバイト単位で直接コピーされますが、カスタム型はコピー コンストラクターを呼び出すことによってコピーされます。

次に、次のような質問があります。

デフォルトのコピーコンストラクタが生成されるということは、コピーコンストラクタを明示的に実装する必要はないということですか?

デフォルトのコピー コンストラクターの値コピーは、構築されたオブジェクトの内容をそのまま残し、コピーされたオブジェクトが元のオブジェクトと同じメモリ空間を指すようにします。

これにより、プログラムの終了時にメモリが複数回解放され、プログラムがクラッシュすることになります。

知らせ:

クラスにリソース アプリケーションが関与していない場合は、コピー コンストラクターを記述してもしなくても構いません。リソース アプリケーションが関与すると、コピー コンストラクターを記述する必要があります。それ以外の場合は浅いコピーになります。

対応するソリューションはディープ コピーです。

  • ディープ コピーは、オブジェクト内の動的メンバーの単純な割り当てではなく、スペースの再割り当て、つまりリソー​​スの再割り当てです。

2.4.3 使用シナリオ

コピー コンストラクターの一般的な呼び出しシナリオ:

  • 既存のオブジェクトを使用して新しいオブジェクトを作成する
  • 関数パラメータの型はクラス型オブジェクトです
  • 関数の戻り値の型がクラス型オブジェクトである

プログラムの効率を上げるため、汎用オブジェクトにパラメータを渡すときは参照型を使用し、返すときは実際のシーンに合わせて可能な限り参照を使用するようにしてください。

2.4.4 オーバーロード

コピー コンストラクターのオーバーロードと通常の関数のオーバーロードには基本的に違いはありません。つまり、パラメーターが異なるため、同じ関数名が異なる関数を表しますが、ここでのコンストラクターには戻り値がありません。

3. デストラクター

3.1 コンセプト

私たちは物体がどのようにしてできたのか、そしてその物体がどのようにして消えたのかを知っていますか?

デストラクター:

コンストラクターの機能とは異なり、デストラクターはオブジェクト自体の破棄を完了しません。ローカル オブジェクトの破棄はコンパイラーによって行われます。

オブジェクトが破棄されると、自動的にデストラクターが呼び出され、オブジェクト内のリソースのクリーンアップが完了します。

3.2 特徴

  1. デストラクター名には、クラス名の前に文字 ~ が付加されます。

  2. パラメータなし、戻り値の型なし

  3. クラスにはデストラクターを 1 つだけ含めることができます。明示的に定義されていない場合、システムはデフォルトのデストラクターを自動的に生成します。

    注: デストラクターはオーバーロードできません

  4. オブジェクトのライフサイクルが終了すると、C++ コンパイル システムは自動的にデストラクターを呼び出します。

class Time
{
public:
 	~Time()
 	{
 		cout << "~Time()" << endl;
 	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
 	// 基本类型(内置类型)
 	int _year = 1970;
 	int _month = 1;
 	int _day = 1;
 	// 自定义类型
 	Time _t;
};
int main()
{
 Date d;
 return 0;
}
//在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
//因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
//但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁
//main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数

3.3 重要なポイント

知らせ:

  1. どのクラスのオブジェクトを作成してそのクラスのデストラクタを呼び出し、そのクラスのオブジェクトを破棄してそのクラスのデストラクタを呼び出します。
  2. クラスにリソース アプリケーションが存在しない場合、デストラクターを記述することはできません。Date クラスなど、コンパイラによって生成されたデフォルトのデストラクターが直接使用されます。リソース アプリケーションがある場合は、それを記述する必要があり、存在しない場合は、 Stack クラスなどのリソース漏洩の原因となる

おすすめ

転載: blog.csdn.net/qq_64893500/article/details/128883749
おすすめ