技術共有

コンストラクター、静的メンバー、フレンド、内部クラスの初期化リスト [クラスとオブジェクト (その 2)]

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

PS: 次のコードは VS2022 環境でテストされていますが、すべてのコンパイラが合格できるというわけではありません。
PS: どのテストコードにもヘッダーファイル stdio.h の宣言は表示されていません。使用する場合は自分で追加してください。

  

ここに画像の説明を挿入します

                                           ブロガーのホームページ: LiUEEEEE
                                                C++ 列
                                              C言語コラム
                                            データ構造列
                                            ソートアルゴリズム列
                                         新受の定番トピックスコラム

1. コンストラクタの初期化リスト

1.1 コンストラクターを振り返る


前回のブログでは

          構築、破壊、コピー【クラスとオブジェクト(その2)】
クラスのコンストラクターについては、「日付クラス」を例に説明します。初期化メソッドは次のとおりです。

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

上記のコードに示されているように、C++ をさらに学習しないと、ユーザーは通常、コンストラクター内のコードがクラス メンバー変数の初期化であると考えるでしょう。しかし、実際はまったく逆で、C++ ではコンストラクターのメイン ステートメントは初期化のみを行います。コンストラクターはメンバー変数に値を複数回代入できるのに対し、初期化は 1 回しか初期化できないため、代入は本当の意味での初期化とは言えません。

	Date(int year, int month, int day)
	{
		_year = year;
		_year += 1;
	}
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 初期化リスト


このとき、C++ の初期化リストが導入されます。
初期化リストは、「:」(コロン) で始まり、「,」(カンマ) で区切られたデータ メンバーのリストであり、各メンバー変数の後に括弧が続き、括弧内に置かれた値が初期化された値になります。 :
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
  • 1
  • 2
  • 3
  • 4
  • 5

注意しなければならないことは次のとおりです。

  1. 各メンバー変数は、初期化リストに 1 回だけ出現できます (初期化は 1 回だけ初期化できます)。
  2. クラスには次のメンバーが含まれており、初期化のために初期化リストに配置する必要があります。
    • メンバー変数の参照
    • カスタム型のメンバー変数 (カスタム型にはデフォルトのコンストラクター (パラメーターなしのコンストラクター、完全なデフォルトのコンストラクター、システムによって自動的に生成されるコンストラクター) がありません)
    • const 変更されたメンバー変数

上記のカスタム メンバー変数 (デフォルト コンストラクターなし) は、定義されたカスタム メンバー変数のコンストラクターがパラメーターを持つコンストラクターであり、ユーザーがコンストラクターを表示するときに初期化する前にパラメーターを渡す必要があることを意味します。ただし、カスタム メンバー変数の初期化にパラメータの参加が必要な場合、システムはデフォルトでパラメータを渡さないため、上記の注意事項が発生します。初期化されていない状況が発生するのを避けるため、その後の使用時に不要なトラブルが発生するのを防ぎます。

1.3 リストの初期化に関するヒント


初期化リストを記述する場合、コンパイラは宣言順に従って変数を順番に初期化するため、メンバ変数の初期化順序はクラス内の宣言順序と一致している必要があります。この順序に従って記述しないと、次のような問題が発生します。不要なトラブル、例えば:
class A
{
public:
	A(int n)
		:_a2(n)
		,_a1(_a2)
	{}
	void Print()
	{
		cout << "_a1 = " << _a1 << endl;
		cout << "_a2 = " << _a2 << endl;
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A aa(100);
	aa.Print();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

ここに画像の説明を挿入します
上図のように初期化すると期待と異なる結果が得られるため、使用する場合にはそのような操作は避けてください。



2. 明示的なキーワード


コンストラクターには、専用の関数もあります。単一パラメータのコンストラクタまたは複数のパラメーターを持つコンストラクターですが、最初のパラメーターのデフォルト値はありません暗黙的な型変換。例:
class Date
{
public:
	Date(int year)
		: _year(year)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

値を渡して代入する場合は、たとえば次のようになります。

	Date date(2024);
  • 1

コンパイラ レベルでは、2024 は作成されたクラス オブジェクトに直接割り当てられませんが、暗黙的な変換を通じて 2024 が一時クラス オブジェクトに変換され、そのようなオブジェクトを通じて初期化が必要なクラス オブジェクトに割り当てられます。が必要です。 初期化されたクラス オブジェクトは A で、一時クラス オブジェクトは B です。

	Date A(2024);
	等价于
	Date A = B//B的值为2024
  • 1
  • 2
  • 3

暗黙的な変換を回避するために、C++ では明示的なキーワードを作成しました。コンストラクターの前に明示的に追加すると、暗黙的な変換が行われるのを防ぐことができます。
	explicit Date(int year)
		: _year(year)
	{}
  • 1
  • 2
  • 3




3. 静的メンバー

3.1 静的メンバーの概念


static として宣言されたクラス メンバーは、クラスの静的メンバーと呼ばれます。static で変更されたメンバー変数は、静的メンバー関数と呼ばれます。静的メンバー変数はクラスの外部で初期化する必要があります。次に例を示します。
class Date
{
public:
	Date(int year, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
	static int _i;
};

int Date::_i = 1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.2 静的メンバーの特性


  1. 静的メンバーはすべてのクラス オブジェクトで共有され、特定のオブジェクトに属しません。静的領域に格納されます。
  2. 静的メンバー変数はクラスの外で定義する必要があります。定義時に static キーワードは追加されません。クラス内で宣言されるだけです。
  3. クラスの静的メンバーには、class name::static member または object.static member を使用してアクセスできます。
  4. 静的メンバー関数にはこのポインターが隠されていないため、非静的メンバーにはアクセスできません。
  5. 静的メンバーもクラスのメンバーであり、public、protected、および private のアクセス修飾子によって制限されます。




4. ヨウユアン


フレンドはカプセル化を突破する方法を提供し、場合によっては利便性を提供します。ただし、フレンドはカップリングを増加させ、カプセル化を破壊するため、フレンドを複数回使用しないでください。

フレンドはフレンド機能とフレンドクラスに分かれています。

4.1 フレンド機能


フレンド関数とは、クラス外の関数がクラスのプライベート メンバーにアクセスする必要がある場合、クラス内でこの関数をクラスのフレンド関数として宣言することでアクセスできることを意味します。次に例を示します。
class Date
{
	friend void Print(Date& d);
public:
	Date(int year = 1, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
void Print(Date& d)
{
	cout << d._year << " " << d._month << " " << d._day <<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • フレンド関数は、クラスのプライベートおよび保護されたメンバーにアクセスできますが、クラスのメンバー関数にはアクセスできません。
  • フレンド関数は const で変更できません
  • フレンド関数はクラス定義内のどこでも宣言でき、クラスのアクセス修飾子によって制限されません。
  • 関数は複数のクラスのフレンド関数になることができます
  • フレンド関数の呼び出し原理は通常の関数と同じです。

4.2 フレンドクラス


クラスが別のクラスのプライベート メンバーにアクセスしたい場合、そのクラスは、アクセスする必要があるクラス内のフレンドとしてこのクラスを宣言し、そのプライベート メンバーにアクセスできます。次に例を示します。
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
					   //中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
			: _hour(hour)
			, _minute(minute)
			, _second(second)
	{}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
	{}	
	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 友人関係は一方通行であり、非交換的です。たとえば、上記の Time クラスと Date クラスで、Time クラス内で Date クラスをそのフレンド クラスとして宣言すると、Date クラス内で Time クラスのプライベート メンバ変数に直接アクセスできますが、 Time クラスの Date クラスのプライベート メンバー変数にアクセスします。
  • 友人関係は推移的であることはできません。C が B の友人であり、B が A の友人である場合、C が A の友人であることを意味することはできません。




5. 内部クラス


C++ は、クラス内に別のクラスを作成することをサポートしています。これは内部クラスと呼ばれます。内部クラスは外部クラスに属さず、外部クラスのオブジェクトを介して内部クラスのメンバーにアクセスすることはできません。 。外部クラスには、内部クラスへの優れたアクセス権はありません。
そして、内部クラスは当然ながら外部クラスのフレンドです。つまり、内部クラスは外部クラスのプライベート メンバーにアクセスできます。

特性:

  1. 内部クラスは、外部クラスで public、protected、または private として定義できます。
  2. 内部クラスは、外部クラスのオブジェクト/クラス名を必要とせずに、外部クラスの静的メンバーに直接アクセスできることに注意してください。
  3. sizeof(external class)=external class、内部クラスとは関係ありません。




6. 結論


ここに画像の説明を挿入します

  私のオリジナル記事をご覧いただきまして誠にありがとうございます。
  この記事は主に個人的な学習と知識の共有に使用されます。学習には長い時間がかかります。間違いがある場合は、修正していただきありがとうございます。
  引用する場合はアドレスを明記してください。