C ++ 2のクラスとオブジェクト-コンストラクタ、デストラクタ、コピーコンストラクタ、代入演算子のオーバーロード関数

クラス1コンストラクター

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

次の日付クラスの場合


class Date{
    
     
public:
 void SetDate(int year, int month, int day)
 {
    
    
 _year = year;
 _month = month;
 _day = day;
 }
 
 void Display()
 {
    
    
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year;
 int _month;
 int _day;
 }
 int main()
{
    
    
 Date d1,d2;
 d1.SetDate(2018,5,1);
 d1.Display();
 
 Date d2;
 d2.SetDate(2018,7,1);
 d2.Display();
 return 0;
 }
 

1上記のDateクラスでは、SetDate publicメソッドを使用してオブジェクトのコンテンツを設定できますが、オブジェクトを作成するたびにこのメソッドを呼び出して情報を設定すると、少し面倒になります。オブジェクトを設定できますかオブジェクトの作成時に情報が自動的に作成されますか?何ですか?
2コンストラクターはこの問題を解決します。コンストラクターはクラス名と同じ名前の特別なメンバー関数です。クラスタイプオブジェクトの作成時にコンパイラーによって自動的に呼び出され、それぞれがプライベートデータメンバーには適切な初期値があり、オブジェクトのライフサイクルで1回だけ呼び出されます。

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

コンストラクターは特別なメンバー関数です。コンストラクターの名前はコンストラクターと呼ばれますが、コンストラクターの主なタスクは
、オブジェクトを作成するためのスペースを開くことではなく、オブジェクトを初期化することです。
その特徴は次のとおりです
。1。関数名はクラス名と同じです
。2。戻り値はありません
。3。オブジェクトがインスタンス化されると、エディターは対応するコンストラクターを自動的に呼び出します。
4.コンストラクターはオーバーロードできます

#include <iostream>
#include <windows.h>
using namespace std;

class Date{
    
    
public:
	//1.无参构造函数
	Date(){
    
    

	}
	// 2.带参构造函数
	Date(int year, int month, int day){
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    

	Date d1; //调用无参构造函数   注意不要带括号:Date d1();否则就成了函数声明
	Date d2(2021, 8, 11);//调用带参构造函数

	system("pause");
	return 0;
}

5.コンストラクターがクラスで明示的に定義されていない場合、C ++コンパイラーはパラメーターなしのデフォルトコンストラクターを自動的に生成します。ユーザーがコンストラクターを明示的に定義すると、コンパイラーはそれを再度生成しません。
6.パラメーターなしのコンストラクターと完全なデフォルトのコンストラクターはどちらもデフォルトのコンストラクターであり、デフォルトのコンストラクターは1つしか存在できません。注:引数なしのコンストラクター、完全なデフォルトコンストラクター、およびコンパイラーによって書き込みなしでデフォルトで生成されたコンストラクターはすべて、デフォルトのコンストラクターと見なすことができます。
次のようなコードはコンパイルエラーになります

#include <iostream>
#include <windows.h>
using namespace std;

class Date{
    
    
public:
	//1.无参构造函数
	Date(){
    
    

	}
	// 2.带参构造函数
	Date(int year = 1000, int month = 23, int day = 12){
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    

	Date d1; //调用无参构造函数   注意不要带括号:Date d1();否则就成了函数声明
	//Date d2(2021, 8, 11);//调用带参构造函数

	system("pause");
	return 0;
}

ここに画像の説明を挿入

7.以下の奇妙な現象を見てみましょう

#include <iostream>
#include <windows.h>
using namespace std;
class Date{
    
    
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1; //调用无参构造函数   注意不要带括号:Date d1();否则就成了函数声明
	system("pause");
	return 0;
}

上記のコードはデフォルトの引数なしコンストラクターを呼び出しますが、結果はこれです。
ここに画像の説明を挿入
コンパイラーによって生成されるデフォルトのメンバー関数について質問する学生がたくさんいます。コンストラクターを実装しない場合、デフォルトのメンバー関数が生成されます。コンパイラによって表示されません。効果は何ですか?データはまだランダムな値です。コンパイラによって生成されたデフォルトのメンバー関数は役に立ちませんか?
実際、これは次のようになります。C++は、型を組み込み型(基本型)とカスタム型に分割します。組み込み型は、
int / char ...などの文法で定義された型であり、カスタム型は、class / struct / unionを使用して定義された型です。次のプログラムを見ると、
コンパイラがデフォルトのコンストラクタを生成することがわかります。カスタム型のmember_tは、デフォルトのメンバー関数を呼び出してオブジェクトtを初期化します(コンパイラ自体のデフォルトでもある場合は、何もしません)。

#include <iostream>
#include <windows.h>
using namespace std;
class Time{
    
    
public:
	Time()
	{
    
    
		cout << "Time()" << endl;
		_hour = 12;
		_minute = 12;
		_second = 12;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date{
    
    
private:
	int _year;
	int _month;
	int _day;
	Time t;
};
int main()
{
    
    
	Date d1; //调用无参构造函数   注意不要带括号:Date d1();否则就成了函数声明
	system("pause");
	return 0;
}

ここに画像の説明を挿入

検索して!

2種類のデストラクタ

2.1デストラクタの概念

コンストラクターの研究を通じて、オブジェクトがどのように発生し、そのオブジェクトがどのように消えるかがわかります。
デストラクタ:コンストラクタの機能とは異なり、デストラクタはオブジェクトの破棄を完了しません。ローカルオブジェクトの破棄はコンパイラによって行われます。オブジェクトが破棄されると、デストラクタが自動的に呼び出され、クラスの一部のリソースのクリーンアップが完了します。

2.2デストラクタの特性

デストラクタは特別なメンバー関数であり、その特徴は次のとおりです。

  1. デストラクタ名の前には、クラス名の文字〜が付いています。
  2. パラメータも戻り値もありません
  3. クラスには、デストラクタが1つだけあります。明示的に定義されていない場合、システムはデフォルトのデストラクタを自動的に生成します。
  4. オブジェクトのライフサイクルが終了すると、C ++コンパイルシステムは自動的にデストラクタを呼び出します。たとえば、次のシーケンステーブルクラス
#include <iostream>
#include <assert.h>
#include <string.h>
#pragma warning(disable:4996)
using namespace std;
class SeqList{
    
    
public:
	SeqList(int capacity){
    
    
		_arr = (int*)(malloc(sizeof(int)*capacity));
		assert(_arr);
		_capacity = capacity;
		_size = 0;
	}
	~SeqList(){
    
    
		if (_arr){
    
    
			cout << "~SeqList()" << endl;
			free(_arr);
			_arr = nullptr;
			_size = _capacity = 0;
		}
		
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};
class MyString{
    
    
public:
	MyString(const char* str = "chenzhihao"){
    
    
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~MyString(){
    
    
		cout << "~MyString()" << endl;
		free(_str);
		_str = nullptr;
	}
private:
	char* _str;
};
class Person{
    
    
private:
	MyString _name;
	int _age;
};
int main()
{
    
    
	SeqList sl(12);
	Person p;
	return 0;
}

ここに画像の説明を挿入

  1. コンパイラによって自動的に生成されるデストラクタに関して、何かが行われます。上記のプログラムソースコードから、コンパイラによって生成されるデフォルトのデストラクタが、カスタムタイプのメンバーに対してそのデストラクタを呼び出すことがわかります。

3種類のコピー/コピーコンストラクタ

3.1コピー/コピーコンストラクタの概念

ここに画像の説明を挿入

オブジェクトを作成するときに、オブジェクトとまったく同じ新しいオブジェクトを作成できますか?
コピーコンストラクター:このタイプのオブジェクトへの参照(通常は定数参照)である仮パラメーターは1つだけです。これは、既存のタイプのオブジェクトで新しいオブジェクトを作成するときにコンパイラーによって自動的に呼び出されます。

3.2コピー/コピーコンストラクタの機能

コピーコンストラクターも特別なメンバー関数であり、その特性は次のとおりです
。1。コピーコンストラクターはコンストラクターのオーバーロード形式です
。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)
 {
    
    
 _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; 
 }

ここに画像の説明を挿入

  1. 明示的に定義されていない場合、コンパイラーはデフォルトのコピーコンストラクターを生成します。デフォルトのコピーコンストラクタオブジェクトは、メモリストレージに従ってバイト順にコピーされます。この種のコピーは、シャローコピーまたは値コピーと呼ばれます。
  2. 次に、コンパイラによって生成されたデフォルトのコピーコンストラクタは、エンディアン値のコピーをすでに完了できますが、それでも自分で実装する必要がありますか?もちろん、日付クラスのようなクラスは必要ありませんが、次のクラスの場合はどうなりますか?コンパイルされたコードは合格しますか?操作にエラーはありますか?
#include <iostream>
#include <assert.h>
#include <string.h>
#pragma warning(disable:4996)
using namespace std;
class MyString{
    
    
public:
	MyString(const char* str = "Tom"){
    
    
		_str = (char*)malloc(strlen(str) + 1);
		strcpy(_str, str);
	}
	~MyString(){
    
    
		cout << "~MyString()" << endl;
		free(_str);
		_str = nullptr;
	}
private:
	char* _str;
};
int main()
{
    
    
	MyString s1("czh");
	MyString s2(s1);
	return 0;
}

答えは次のとおりです。コンパイルは問題ありませんが、デフォルトのコピーコンストラクタは値(浅い)コピーであり、_strは同じであるため、プログラムの実行はクラッシュします。つまり、メモリスペースを指し、デストラクタはで呼び出されます。 s2その時点で、_strが指すスペースが解放され、次にs1がデストラクタを呼び出してこのスペースを解放します。この時点で、このスペースはオペレーティングシステムによって再利用されています。このスペースはs1に属していません。つまり、属していないものを解放します。スペースが原因で、プログラムがクラッシュします。

4種類の代入演算子のオーバーロード関数

4.1演算子のオーバーロード

C ++では、コードの可読性を高めるために演算子のオーバーロードが導入されています。演算子のオーバーロードは、特別な関数名を持つ関数であり、戻り値のタイプ、関数名、およびパラメーターリストもあります。戻り値のタイプはパラメーターリストと同様です。および通常の機能。
関数の名前は次のとおりです。キーワード演算子+オーバーロードする必要のある演算子記号。
関数プロトタイプ:戻り値型演算子演算子(パラメーターリスト)
注:
他の演算子を接続して新しい演算子を作成することはできません。たとえば、operator @
オーバーロード演算子には、クラス型または列挙型のオペランドが必要です。
組み込み型に使用される演算子の意味は変更できません。たとえば、組み込み整数+は、その意味を変更できません。
クラスメンバーのオーバーロードされた関数として使用される場合、仮パラメーターリストの最初のパラメーターは次のとおりです。このポインタを入力します。*
:: szieof?:。これらの5つの演算子はオーバーロードできません*。

class Date
{
    
     
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
    
    
 _year = year;
 _month = month;
 _day = day;
 }
 
 // bool operator==(Date* this, const Date& d2)
 // 这里需要注意的是,左操作数是this指向的调用函数的对象
 bool operator==(const Date& d2)
 {
    
    
 return _year == d2._year;
 && _month == d2._month
 && _day == d2._day;
 }
 private:
 int _year;
 int _month;
 int _day;
}
void Test ()
{
    
    
 Date d1(2018, 9, 26);
 Date d2(2018, 9, 27);
 cout<<(d1 == d2)<<endl; 
 } 

4.2代入演算子のオーバーロード

代入演算子には、主に次の点に注意してください。

  1. パラメータタイプ
  2. 戻り値
  3. 自分に値を割り当てているかどうかを確認します
  4. * thisを返す
  5. クラスが代入演算のオーバーロードを明示的に定義していない場合、コンパイラはオブジェクトのエンディアン値(浅い)コピーを完了するために1つも生成します。

次に、コンパイラによって生成されたデフォルトの代入オーバーロード関数は、エンディアン値のコピーを完了することができますが、それでも自分で実装する必要がありますか?もちろん
、日付のようなクラスは必要ありません。次のクラスはどうですか?確認してみますか?

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
class String
{
    
    
public:
 String(const char* str = "")
 {
    
    
 _str = (char*)malloc(strlen(str) + 1);
 strcpy(_str, str);
 }
 ~String()
 {
    
    
 cout << "~String()" << endl;
 free(_str);
 }
 private:
 char* _str;
};
int main()
{
    
    
 String s1("hello");
 String s2("world");
 
 s1 = s2; 
 }

ここでは、上記のコピーコンストラクタと同じ状況が発生し、プログラムがクラッシュします。理由は、繰り返しの破棄によって引き起こされる問題でもあり、解決するにはディープコピーが必要です。後で説明します。

おすすめ

転載: blog.csdn.net/CZHLNN/article/details/113736980