C++のコンストラクタとデストラクタの詳しい説明

概要

C++ では、オブジェクトの初期化とクリーンアップにコンストラクターとデストラクターが使用され、これら 2 つの関数はコンパイラーによって自動的に呼び出されます。オブジェクトの初期化とクリーンアップは非常に重要です。コンストラクターとデストラクターを提供しない場合、コンパイラーは自動的に 2 つの関数の空の実装を提供します。

コンストラクター: 主に関数を作成するときにオブジェクトのメンバーのプロパティに値を割り当てるために使用されます。
デストラクター: 主に、オブジェクトが破棄される前に、何らかのクリーニング作業 (ヒープ領域で new によって開かれたスペースを解放するなど) を実行するために使用されます。

主な特徴:
コンストラクター構文: クラス名 (){}
1. コンストラクター、戻り値なし、void なし
2. 関数名はクラス名と同じ
3. コンストラクターにはパラメーターを含めることができるため、オーバーロードが発生する可能性があります
4. プログラムオブジェクトを呼び出すと、コンストラクターが自動的に呼び出されます。手動で呼び出す必要はなく、一度だけ呼び出されます。

デストラクタ構文: ~class name(){}
1. デストラクタ、戻り値なし、void なし
2. 関数名はクラス名と同じで、名前の前に記号が追加されます~
3. デストラクタにはパラメータを含めることはできません。 4.
プログラムはオブジェクトが破棄される前に自動的にデストラクターを呼び出します。手動で呼び出す必要はなく、一度だけ呼び出されます。

その他の機能:

  • コンストラクターとデストラクターは特別なパブリック メンバー関数であり、各クラスにはデフォルトのコンストラクターとデストラクターがあります。
  • コンストラクターは、クラスが定義されるときにシステムによって自動的に呼び出され、デストラクターはクラスが破棄されるときにシステムによって自動的に呼び出されます。
  • コンストラクターの名前はクラスの名前と同じですクラスには複数のコンストラクターを含めることができますが、デストラクターは 1 つだけですさまざまなコンストラクターは、パラメーターの数とパラメーターの型によって区別されます。
  • コンストラクターでクラスにリソースを割り当て、クラスのデストラクターで対応するリソースを解放できます。
  • プログラマが構築と破壊を提供しない場合、システムはデフォルトで空の実装を提供します。
  • コンストラクターとデストラクターは呼び出す前にパブリックで定義する必要があります

簡単な例:

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	Person()
	{
    
    
		cout << "调用构造函数" << endl;
	}
	~Person()//对象销毁前,自动调用
	{
    
    
		cout << "析构函数的调用" << endl;
	}
};
void test()
{
    
    
	//创建对象
	Person  p;//这是一个局部变量,test执行完毕后会释放该对象 进而调用析构函数
}
int main()
{
    
    
	Person  p1;//如果在main函数创建对象,析构函数会在按任意键后再调用
	test();
	system("pause");
	return 0;
}

実行結果:
ここに画像の説明を挿入
プログラムが最初にオブジェクト p1 のコンストラクターを呼び出し、次にオブジェクト p のコンストラクターとデストラクターを呼び出します。p1 は main 関数の一部であるため、任意のキーを押した後に p1 のデストラクターが表示されることがわかります。変数は main 関数の終了時に解放され、解放時に p1 のデストラクターが呼び出されます。

コンストラクターの分類

2 つの分類方法:

  • パラメータによる分割: パラメータ化された構築とノンパラメトリックな構築

  • 通常施工とコピー施工の種類別

3 つの呼び出し方法:

  • ブラケット (最も一般的)

  • 明示的な方法

  • 暗黙的な変換方法

パラメーター付き、パラメーターなし、およびコピーの 3 つのコンストラクターについて、メソッド呼び出しの例が 3 つあります (パラメーターなしで呼び出すメソッドは 1 つだけです)。

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	Person()
	{
    
    
		cout << "调用无参(默认)构造函数" << endl;
	}
	Person(int a)
	{
    
    
		age = a;
		cout << "调用有参构造函数" << endl;
	}
	//拷贝构造函数用于拷贝传入的类到自身类上 
	//除了拷贝构造函数外都是普通构造函数
	Person(const Person &p)//传入的类不希望被改变所以加const 传入引用用p指向该类
	{
    
    
		age = p.age;
		cout << "调用拷贝构造函数" << endl;
	}
	~Person()
	{
    
    
		cout << "析构函数的调用" << endl;
	}
	int age;

};
void test()
{
    
    
    //调用
    //1.括号法
	//注意:调用无参构造时不要输入()
	//Person p();会被编译器认为是函数的声明
	Person p;//调用无参构造函数
	Person p1(10);//调用有参函数构造
	Person p2(p1);//调用拷贝构造函数
	cout <<"p1的年龄"<< p1.age << endl;
	cout <<"p2的年龄"<< p2.age << endl;
	
	//2.显式法
	Person p3;//调用无参构造函数
	Person p4=Person (10);//调用有参函数构造
	Person p5=Person (p1);//调用拷贝构造函数
	//Person(10)为匿名对象 等号左侧就是它的名 
	//特点:当前行结束时,系统会立即回收掉匿名对象 即它的析构函数会在该行结束后就调用而不是test函数结束
	
	//3.隐式转换法
	Person p6 = 10; //调用有参函数构造 相当于Person p6=Person(10);  假如有两个参数就是 Person p6 = (10,9);
	Person p7 = p1;//调用拷贝构造函数 相当于Person p7=Person(p1);
}
int main()
{
    
    
	test();
	system("pause");
	return 0;
}

1. 引数なし (デフォルト) コンストラクター

たとえば、パラメーターのないコンストラクターは、年齢と身長、年齢 (整数)、身長 (整数ポインター) の 2 つの属性を含む Person クラスを作成します。デフォルトの構築の初期化では年齢は 0、身長は 0 です。ユーザーが実装しない場合、システムはデフォルトで空の実装になります。

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	//默认构造函数
	Person();
	int m_Age;
	int* m_Height;
};
//初始化年龄和身高
Person::Person() 
{
    
    
	cout << "默认构造函数的调用!" << endl;
	this->m_Age = 0;
	this->m_Height = new int(0);
}
int main()
{
    
    
	Person p;
	cout << "此人的年龄是: " << p.m_Age << endl;
	cout << "此人的身高是: " << *(p.m_Height) << endl;
	return 0;
}

オブジェクトを作成するとき、それは Person p であることに注意してください。このように Person p() と書くと、コンパイラは関数 p が作成され、戻り値の型は Person で、仮パラメータは空であるとみなします。 {} は実装するには空であるため、これに注意する必要があります。

結果:
ここに画像の説明を挿入

2. パラメータ付きコンストラクタ

パラメーター化された構造を導入しましょう。または、引き続き上記の例を使用して追加します。指定された年齢と高さのオブジェクトを作成したい場合は、パラメーター化された構造を使用する必要があります。例:

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	//默认构造函数
	Person();
	//有参构造
	Person(int age, int height);
	int m_Age;
	int* m_Height;
};
//默认构造
Person::Person() 
{
    
    
	cout << "默认构造函数的调用!" << endl;
	this->m_Age = 0;
	this->m_Height = new int(0);
}
//有参构造,把age赋值给m_Age,身高用m_Height指向
Person::Person(int age,int height)
{
    
    
	cout << "有参构造函数的调用!" << endl;
	this->m_Age = age;
	this->m_Height = new int(height);
}
int main()
{
    
    
	Person p(18,175);
	cout << "此人的年龄是: " << p.m_Age << endl;
	cout << "此人的身高是: " << *(p.m_Height) << endl;
	return 0;
}

ここに画像の説明を挿入

3. コンストラクターの委任

名前が示すように、委任されたコンストラクターは、自分で構築したものを他のコンストラクターに引き渡すことを目的としています。たとえば、デフォルトのコンストラクターを変更すると、

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	//默认构造函数
	Person();
	//有参构造
	Person(int age, int height);
	//拷贝构造
	Person(const Person& p);
	int m_Age;
	int* m_Height;
};
//默认构造
Person::Person() :Person(0, 0)
{
    
    
	cout << "委托构造函数的调用!" << endl;
}
//有参构造,把age赋值给m_Age,身高用m_Height指向
Person::Person(int age, int height)
{
    
    
	cout << "有参构造函数的调用!" << endl;
	this->m_Age = age;
	this->m_Height = new int(height);
}
//拷贝构造函数调用
Person::Person(const Person& p)
{
    
    
	cout << "拷贝构造函数的调用!" << endl;
	this->m_Age = p.m_Age;
	this->m_Height = new int(*p.m_Height);
}
int main()
{
    
    
	Person p;
	cout << "p的年龄是: " << p.m_Age << endl;
	cout << "p的身高是: " << *(p.m_Height) << endl;
	return 0;
}

ここに画像の説明を挿入

4. コピー(コピー)コンストラクター

上記は先頭に属性の値を初期化するもので、コピーコンストラクタは先頭にオブジェクトを渡し、そのオブジェクトの属性をこのオブジェクトにコピーし、オブジェクトを初期化します(渡されたパラメータは羊(コピー構築後に新たな羊がクローンされ、二匹の羊の属性は同じ) これによりオブジェクトの作成が実現できます。

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	//默认构造函数
	Person();
	//有参构造
	Person(int age, int height);
	//拷贝构造
	Person(const Person& p);
	int m_Age;
	int* m_Height;
};
//默认构造
Person::Person()
{
    
    
	cout << "默认构造函数的调用!" << endl;
	this->m_Age = 0;
	this->m_Height = new int(0);
}
//有参构造,把age赋值给m_Age,身高用m_Height指向
Person::Person(int age, int height)
{
    
    
	cout << "有参构造函数的调用!" << endl;
	this->m_Age = age;
	this->m_Height = new int(height);
}
//拷贝构造函数调用
Person::Person(const Person& p)
{
    
    
	cout << "拷贝构造函数的调用!" << endl;
	this->m_Age = p.m_Age;
	this->m_Height = new int(*p.m_Height);
}
int main()
{
    
    
	Person p1(18, 175);
	cout << "p1的年龄是: " << p1.m_Age << endl;
	cout << "p1的身高是: " << *(p1.m_Height) << endl;
	Person p2(p1);//将对象p1复制给p2。注意复制和赋值的概念不同 
	cout << "p2的年龄是: " << p2.m_Age << endl;
	cout << "p2的身高是: " << *(p2.m_Height) << endl;
	return 0;
}

ここに画像の説明を挿入

コピー コンストラクターは、デフォルトで値のコピー (浅いコピー) を実装します。ここではディープコピー構造を使用しています。これにより、デストラクターによってヒープ領域のメモリが繰り返し解放されるのを防ぎ、コードの安全性を確保できます。時間があるときに深いコピーと浅いコピーを再編成します。コピーの前に const 制限を追加すると、メンバー属性の変更を防ぎ、完全な一貫性を確保できます。これは左辺値参照を使用して代入を実行します。上記は左辺値パラメータの参照コピーです。

【考え】
コンストラクタのパラメータをconstなしでコピー(コピー)しても大丈夫でしょうか?const の役割は何ですか
? const を追加するのが最善です。定数左辺値参照は左辺値、右辺値、定数左辺値、および定数右辺値を受け入れることができますが、左辺値参照は左辺値のみを受け入れることができます。
Integer a(Integer(100)); など、一時オブジェクトをコピーする必要がある場合、const を指定しないコピー コンストラクターはエラーを報告します。

https://blog.csdn.net/jiaojinlin/article/details/124744513を参照してください。

5. コンストラクターの移動

この部分の紹介は少なくありません。詳細な紹介については別の記事に移動してください: C++ 移動コンストラクターの詳細な説明

コンストラクター呼び出しルール

デフォルトでは、C++ コンパイラは少なくとも 3 つの関数をクラスに追加します。

1. デフォルトのコンストラクター (パラメーターなし、関数本体は空)

2. デフォルトのデストラクター (パラメーターなし、関数本体は空)

3. プロパティの値をコピーするデフォルトのコピー コンストラクター

コンストラクターの呼び出し規則は次のとおりです。

ユーザーがパラメーターを使用してコンストラクターを定義した場合、C++ はデフォルトの引数なしの構造を提供しなくなりましたが、デフォルトのコピー構造が提供されます。

ユーザーがコピー コンストラクターを定義した場合、C++ は他のコンストラクターを提供しなくなります

#include<iostream>
using namespace std;
class Person {
    
    
public:
	//无参(默认)构造函数
	Person() 
	{
    
    
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int a)
	{
    
    
		age = a;
		cout << "有参构造函数!" << endl;
	}
	//拷贝构造函数
	Person(const Person& p) 
	{
    
    
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}
	//析构函数
	~Person() 
	{
    
    
		cout << "析构函数!" << endl;
	}
public:
	int age;
};

void test01()
{
    
    
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

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

void test02()
{
    
    
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	Person p1; //此时如果用户自己没有提供默认构造,会出错
	Person p2(10); //用户提供的有参
	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

	//如果用户提供拷贝构造,编译器不会提供其他构造函数
	Person p4; //此时如果用户自己没有提供默认构造,会出错
	Person p5(10); //此时如果用户自己没有提供有参,会出错
	Person p6(p5); //用户自己提供拷贝构造
}

int main() {
    
    

	test01();

	system("pause");

	return 0;
}

デストラクタ

デストラクターは、オブジェクトが削除される前にオブジェクトをクリーンアップするために使用され、オブジェクトのライフサイクルが終了しようとすると自動的に呼び出されます。(デストラクタはオブジェクトをクリーンアップしてメモリを解放できます)
デストラクタは~加上类名() を操作し、その目的はデータをクリアしてメモリを解放することです。
以下に例を示します。デフォルトのコンストラクター p を作成し、デフォルトの構築を実行してから、表示情報関数を呼び出して情報を出力し、最後に関数を破棄してコンテンツを解放してメモリ リークを防ぎます。

#include<iostream>
using namespace std;
class Person
{
    
    
public:
	//默认构造函数
	Person();
	//有参构造
	Person(int age, int height);
	//拷贝构造
	Person(const Person& p);
	//移动构造函数
	Person(Person&& p);
	//析构函数
	~Person();
	//展示信息
	void ShowPerson();
	int m_Age;
	int* m_Height;
};
//默认构造
Person::Person()
{
    
    
	cout << "默认构造函数的调用!" << endl;
	this->m_Age = 0;
	this->m_Height = new int(0);
}
//有参构造,把age赋值给m_Age,身高用m_Height指向
Person::Person(int age, int height)
{
    
    
	cout << "有参构造函数的调用!" << endl;
	this->m_Age = age;
	this->m_Height = new int(height);
}
//拷贝构造函数调用
Person::Person(const Person& p):m_Age(p.m_Age),m_Height(new int (*p.m_Height))
{
    
    
	cout << "拷贝构造函数的调用!" << endl;
	/*this->m_Age = p.m_Age;
	this->m_Height = new int(*p.m_Height);*/
}
//移动构造函数
Person::Person(Person&& p)
{
    
    
	cout << "移动构造函数的调用!" << endl;
	this->m_Age = p.m_Age;
	this->m_Height = p.m_Height;
	p.m_Height = NULL;
}
//展示信息
void Person::ShowPerson()
{
    
    
	cout << "年龄是: " << this->m_Age << endl;
    cout << "身高是: " << *(this->m_Height) << endl;
}
Person::~Person()
{
    
    
	cout << "析构函数的调用!" << endl;
	if (this->m_Height != NULL)
	{
    
    
		delete this->m_Height;
	}	
}
//返回一个右值的对象
Person GetPerson()
{
    
    
	Person p;
	return p;
}
int main()
{
    
    
	Person p;
	p.ShowPerson();
	return 0;
}

ここに画像の説明を挿入
【参考鸣谢】
https://blog.csdn.net/qq_44768163/article/details/114661958
https://blog.csdn.net/hbbfvv1h/article/details/115371496
https://blog.csdn.net/m0_61973596/ Article/details/123458069【优质】
https://blog.csdn.net/qqyuanhao163/article/details/101396233
https://blog.csdn.net/weixin_39270987/article/details/116306065

https://blog.csdn.net/zyq11223/article/details/48766515#
https://blog.csdn.net/u014201706/article/details/103032246
https://blog.csdn.net/qq_34170700/article/details /107214554
https://blog.csdn.net/yi_chengyu/article/details/120775215

おすすめ

転載: blog.csdn.net/weixin_44788542/article/details/126187645