記事ディレクトリ
序文
このブログは C++ の知識をまとめたもので、クラスの 6 つのデフォルトのメンバー関数について学びましょう。
以下では主に date クラスを例として使用します。
1. コンストラクター
コンセプト
コンストラクターは、クラスと同じ名前を持つ特別なメンバー関数です。コンストラクターは、クラス型オブジェクトの作成 (クラスのインスタンス化) 時にコンパイラーによって自動的に呼び出され、各データ メンバーが適切な初期値を持ち、オブジェクト全体で使用されるようにします。ライフタイム。サイクル中に 1 回だけ呼び出されます。
コンストラクターは特別なメンバー関数です。コンストラクターの名前はコンストラクターと呼ばれていますが、コンストラクターの主なタスクは、オブジェクトを作成するためのスペースを開くことではなく、オブジェクトを初期化することであることに注意してください。
#include <iostream>
using namespace std;
class Date
{
public:
//构造函数 函数名与类相同 无返回值
Date()
{
// 提示:已调用构造函数
cout << "Date()" << endl;
_year = 0;
_month = 0;
_day = 0;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
d.Print();
return 0;
}
結果は次のようになります。
クラス型のオブジェクトを作成するときに、コンパイラによってコンストラクターが自動的に呼び出されます。
特性
- 1. 関数名がクラスと同じである
- 2.戻り値なし
- 3. コンパイラーは、オブジェクトがインスタンス化されるときに、対応するデフォルトのコンストラクターを自動的に呼び出します。
- 4.パラメーターのないコンストラクター、すべてのデフォルト コンストラクター、およびデフォルトでコンパイラーによって生成されたコンストラクターはすべてデフォルト コンストラクターです (1 つだけありえます)
- 5. クラスに明示的に定義されたコンストラクターがない場合、C++ コンパイラーはパラメーターのないデフォルト コンストラクターを自動的に生成します (組み込み型は処理されず、カスタム型は対応するデフォルト コンストラクターを呼び出します)。ユーザーがコンストラクターを明示的に定義すると、コンパイル ジェネレーターは生成されなくなります。
- 6. コンストラクターはオーバーロードできる
- 7. C++11 では、クラス内の組み込み型メンバー変数の宣言にデフォルト値を与えることができます (それを置き換える初期化リストがあります)。
例:
class Date
{
public:
//构造函数 函数名与类相同 无返回值
// 默认构造函数
//Date()
//{
// // 提示:已调用构造函数
// cout << "Date()" << endl;
// _year = 0;
// _month = 0;
// _day = 0;
//}
// 默认构造函数
/*Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}*/
// 非默认构造函数
/*Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}*/
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
d.Print();
return 0;
}
:特徴4:
特徴3:
注:
- パラメーターなしのコンストラクターを使用してオブジェクトを作成する場合、オブジェクトの後に () [関数呼び出し] を続ける必要はありません。そうしないと、コンパイラーはそれを関数宣言として認識します。
- すべてデフォルトのコンストラクターを使用してパラメーターを渡さずにオブジェクトを作成する場合、オブジェクトの後に () [関数呼び出し] を続ける必要はありません。そうしないと、コンパイラーはそれを関数宣言として認識します。
2. デストラクター
コンセプト
デストラクタ: コンストラクタの機能とは異なり、デストラクタはオブジェクト自体の破棄を完了しません。ローカル オブジェクトの破棄はコンパイラによって完了します。オブジェクトが破棄されると、デストラクタが自動的に呼び出され、オブジェクトの破棄が完了します。オブジェクト内のリソースクリーンアップ作業 (つまり、主に動的に適用されたリソースを破棄します)
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
cout << "Date(int year = 0, int month = 0, int day = 0)" << endl;
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
// 析构函数
~Date()
{
cout << "~Date()" << endl;
_year = 0;
_month = 0;
_day = 0;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2023, 9, 12);
d.Print();
return 0;
}
特性
- デストラクター名の前には ~ という文字が付きます。
- パラメーターも戻り値の型もありません
- クラスに含めることができるデストラクターは 1 つだけです。デストラクターが明示的に定義されていない場合、システムはデフォルトのデストラクターを自動的に生成します。
- オブジェクトのライフサイクルが終了すると、C++ コンパイラは自動的にデストラクターを呼び出します。
- 組み込み型の場合、デストラクターはそれらを処理しません。組み込み型の場合、デストラクターは対応するデストラクターを呼び出します。
- クラスにリソースが適用されていない場合(例の date クラスなど)、デストラクタを記述する必要はなく、コンパイラによって生成されたデフォルトのデストラクタが直接使用されます。そのため、デストラクターを記述する必要があります (すべてのポインター、型はすべて組み込み型であり、コンパイラーによって生成されたデストラクターはポインターが指すスペースをクリーンアップしません)。そうしないと、リソース リークが発生します。
では、コンパイラーによって生成されたデストラクターは何に役立つのでしょうか?
class A
{
public:
A(int a = 0)
{
cout << "A(int a = 0)" << endl;
_a = a;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Date
{
private:
// 内置类型
int _year = 1;
int _month;
int _day;
// 自定义类型
A _aa;
};
int main()
{
Date d;
return 0;
}
上に示したように、コンパイラーによって自動的に生成されたデストラクターは、カスタム型を処理するときに、対応するデストラクターを自動的に呼び出すことができるため、ネストされた
データ構造 (キューをシミュレートするための 2 つのスタックなど) の場合は、これを行う必要はありません。構造体については、デフォルトでコンパイラによって生成されたものを使用してください。
3. コンストラクターのコピー
コンセプト
コピー構築: コピー コンストラクターは、単一の仮パラメーターのみを持つコンストラクターのオーバーロードされた形式です。この仮パラメーターは、このクラスのオブジェクトへの参照です (通常、const 変更でよく使用されます)。既存のクラスを使用して新しいオブジェクトを作成する場合type オブジェクト コンパイラによって自動的に呼び出されます
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
cout << "Date(const Date& d)" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void func(Date d1)
{
cout << "void func(Date d1)" << endl;
}
int main()
{
Date d(2023,9,11);
func(d);
return 0;
}
特性
- コピー コンストラクターは、コンストラクターのオーバーロードされた形式です。
- コピー コンストラクターにはパラメーターが 1 つだけあり、それはクラス オブジェクトへの参照である必要があります。参照ではなく値渡しメソッドが使用されている場合、無限再帰が発生するため、コンパイラーは直接エラーを報告します。 。
パラメーターを参照渡ししないと無限再帰が発生するのはなぜですか?
まず、コピー コンストラクターの 3 つの呼び出しシナリオを知る必要があります。
- 既存のオブジェクトを使用して新しいオブジェクトを作成する
- 関数パラメータの型はクラス型オブジェクトです(仮パラメータを作成します)
- 関数の戻り値はクラス型オブジェクトです (関数の戻り値を保存するために一時オブジェクトが作成されます)
したがって、コピー コンストラクターのパラメーターが参照型ではない場合は、次のようになります。
- コピー コンストラクターが明示的に定義されていない場合、コンパイラーはデフォルトのコピー コンストラクターを生成します。デフォルトのコピー コンストラクターは、メモリ ストレージに従ってバイト オーダー (浅いコピー) でオブジェクトをコピーします。
- デフォルトでコンパイラーによって生成されるコピー コンストラクターは、組み込み型とカスタム型の両方の浅いコピーです。
日付クラスなど、リソース要求を持たないクラスの場合は、コンパイラーによって自動的に生成されたコピー コンストラクターを使用します。
リソース アプリケーション (スタック、キュー、シーケンス テーブルなど) を持つクラスの場合、ディープ コピーが完了している必要があり、シャロー コピーは使用できません
以下のように:
コンパイラによって自動的に生成されたコピー構造を使用します。
これは、オブジェクト a1 がオブジェクト a の浅いコピーであるため、オブジェクト a._date とオブジェクト a1._date が動的に適用された同じスペースをポイントしているためです。オブジェクト a1 が破棄されると、スペースは解放され、オブジェクト a が破棄されると、スペースが解放されます。この領域を解放するとエラーが発生します。
正しい書き方は、オブジェクト a1 が別のスペースに適用され、オブジェクト a が指すコンテンツをスペースにコピーするようにディープ コピーを完了することです。
4. 代入演算子のオーバーロード
演算子のオーバーロード
C++ では、読みやすさを向上させるために演算子のオーバーロードが導入されています。演算子のオーバーロードは、特殊な関数名を持つ関数です。関数名は次のとおりです: キーワード演算子の後に、オーバーロードする必要がある演算子記号が続きます。関数のプロトタイプ: 戻り値の型の
演算
子演算子 (パラメータリスト) は
次のとおりです: 日付クラスの未満記号 (<) をオーバーロードしました
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
// 返回值类型 operator操作符 参数
bool operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
return false;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 9, 12);
Date d2(2022, 9, 12);
cout << (d1 < d2) << endl;
return 0;
}
知らせ:
- 他のシンボルをリンクして新しい演算子を作成することはできません
- オーバーロードされた演算子のパラメータにはクラス型が必要です
- 意味を変更できない組み込み型の演算子
- クラス メンバー関数としてオーバーロードされると、その仮パラメーターはオペランドの数より 1 つ減ったように見えます (このポインターは非表示になります)。
- ( .* / :: / sizeof / ? : / . ) これら 5 つの演算子はオーバーロードできません
- 日付クラス (Date& 演算子++()) などのプレフィックス ++ 演算子のオーバーロードの場合
- 日付クラス (Date& 演算子++(int)) などの後置 ++ 演算子のオーバーロードの場合
代入演算子のオーバーロード
代入演算子のオーバーロード形式:
- パラメータの種類: const T&、参照を渡すことでパラメータの受け渡しの効率が向上します。
- 戻り値のタイプ: T&。参照を返すと、戻り効率が向上します。値を返す目的は、継続的な代入をサポートすることです。
- 自分自身に値を割り当てているかどうかを確認する
- Return *this: 連続代入の意味を複合化するため
知らせ:
- 代入演算子はクラスのメンバー関数としてのみオーバーロードでき、グローバル関数としてオーバーロードできません。
理由: 代入演算子がクラスで明示的に定義されていない場合、コンパイラはデフォルトの関数を自動的に生成します。このとき、クラス外のユーザーが定義・実装した代入演算子オーバーロード関数と競合するため(フレンド関数を使用するか、メンバー変数をpublicにする必要がある)、オーバーロードされた代入演算子はクラスのメンバー関数にしかなれません。クラス。
- コンパイラによって生成された代入演算子のオーバーロードは、組み込み型とカスタム型をバイトごとに値ごとにコピーします。(組み込み型は直接コピーされ、カスタム型のメンバー変数は、代入を完了するために対応する代入演算子のオーバーロードを呼び出す必要があります)
コピー構築と同様に、リソース要求のある型の場合は、代入演算子のオーバーロードを完了するためにディープ コピーの取得を実装する必要があります。
コンパイラはデフォルトで生成します
自己実現した
5. アドレス取得演算子と const アドレス取得演算子のオーバーロード
定数メンバー
const 変更されたメンバー関数は、const メンバー関数と呼ばれます。const 変更されたクラス メンバー関数は、実際にはメンバー関数内で暗黙的に this ポインターを変更し、メンバー関数内でクラスのメンバーを変更できないことを示します。
知らせ:
- const によって変更されたオブジェクトは const メンバー関数のみを呼び出すことができますが、const 関数の呼び出しによって変更されていないメンバー関数を呼び出すことはできません。
- const によって変更されたオブジェクトの場合は、通常のメンバー関数または const メンバー関数を呼び出すことができます。
- メンバー関数が const メンバー関数としてオーバーロードされている場合、コンパイラーは関数の呼び出し時に最適な一致を行います。
アドレス取得演算子と const アドレス取得演算子のオーバーロード
これら 2 つのデフォルトのメンバー関数は通常、自分で完成させる必要はなく、デフォルトでコンパイラによって生成されたものを使用するだけです。
ただし、他の人にオブジェクトのアドレスを取得させたくない場合、または指定されたアドレスを他の人に取得させたくない場合は、これら 2 つの関数を自分で実装できます。
class A
{
public:
A* operator&()
{
return this;
}
const A* operator&() const
{
return this;
}
private:
int _a;
};
要約する
上記は、クラスの 6 つのデフォルトのメンバー関数の要約です。ご支援ありがとうございます!!!