この記事の主な内容は、コンストラクターの初期化リストと、シーケンス テーブルでの演算子のオーバーロードの簡単な適用です。演算子のオーバーロードは、カスタム タイプのストリーム挿入とストリーム抽出を実装します。みなさんのお役に立てれば幸いです、いいね+ブックマーク+コメント、サポートしてください!
目次
(3) カスタム型のメンバーにデフォルトのコンストラクターがない場合の問題
コンストラクターの高度な理解
前の記事では、コンストラクターがオブジェクトを初期化することを説明しました。
コンストラクターはオブジェクトを初期化することのみを指示するため、初期化、つまりメンバー変数の定義の前にオブジェクトにメモリ領域が割り当てられている必要があることに注意してください。メンバー変数の定義はいつ完了しますか? これはコンストラクター内の初期化子リストです。
初期化リストの構文規則は次のとおりです。
class A//类A
{
public:
A()
{
cout<<"调用A的默认构造 A()"<<endl;
_a=i;
}
A(int i)
{
cout << "调用A的带参构造 A(int)" << endl;
_a = i;
}
private:
int _a;
};
class Date//类Date
{
public:
Date(int year = 1, int month = 1, int day = 1)//构造函数
:_year(year),//构造函数的初始化列表(为成员变量分配空间)
_month(month),
_day(day),
a()
{
//构造函数函数体
}
private:
//成员变量的声明
int _year;//内置类型成员
int _month;
int _day;
A a;//自定义类型成员
};
初期化リストはコンストラクターにとって必須であり、手動で記述しなくてもコンパイラーによってデフォルトで追加されます。デフォルトで追加される初期化子リストは、組み込み型に領域を割り当てますが、値 (ランダムな値) を指定しません。カスタム型のデフォルトのコンストラクターは、カスタム型のメンバーに対して呼び出されます。
public:
Date(int year = 1, int month = 1, int day = 1)//构造函数
/*:_year(), //编译器默认添加,会为成员分配内存空间,是随机值
_month(),
_day(),
a()*/ //对自定义类型会调用自定义类型的默认构造函数
{
}
1. パラメータリスト内の組み込み型メンバの定義
現時点では、上記のコンストラクターは初期化リストを手動で追加せず、この時点でオブジェクトが作成されます。
Date d1;
d1.Print();
d1 の組み込み型メンバー変数を出力します。
デフォルトの初期化リストは組み込み型のメンバー変数にのみメモリ領域を割り当て、メンバー変数に値を割り当てないため、ランダムな値になります。
このとき、割り当て操作は手動で追加した初期化リストまたは関数本体で完了できます。
(1). 手動で追加した初期化リストの割り当てを完了します。
Date(int year = 1, int month = 1, int day = 1)//构造函数
:_year(year),
_month(month),
_day(day)
{
}
Date d1;
d1.Print();
操作結果:
(2). 関数本体内で代入が完了
public:
Date(int year = 1, int month = 1, int day = 1)//构造函数
/* :_year(),
_month(),
_day()*/ //默认参数列表
{
_year = year;
_month = month;
_day = day;
}
Date d1;
d1.Print();
操作結果:
概要: 組み込み型メンバ変数の定義(メモリ空間の確保)は、初期化リスト内で一度しか完了できません 書き込みの有無に関わらずこの処理が発生します 定義時に手動書き込みを指定可能。コンパイラーのデフォルトのパラメーター・リストを記述しないと、メンバー変数がランダムな値として定義され、関数本体で必要な値が再度割り当てられます。
2. パラメータリスト内のカスタム型メンバーの定義
初期化リストを手動で追加しない場合、デフォルトの初期化リストはカスタム型メンバーのデフォルトのコンストラクターを呼び出します。
例: コンストラクターが次のような場合
Date(int year = 1, int month = 1, int day = 1)//构造函数
//:_year(),//默认初始化列表
//_month(),
//_day(),
//a() 默认构造函数的调用
{
//构造函数函数体
}
Date d1;
操作結果:
初期化リストを手動で記述する場合、カスタム型のコンストラクター (パラメーター化されたコンストラクターまたはデフォルトのコンストラクター) を選択的に呼び出すことができます。
Date(int year = 1, int month = 1, int day = 1)//构造函数
:_year(year),//默认初始化列表
_month(month),
_day(day),
a(2) //选择调用带参构造函数
現時点では
Date d1;
演算結果
概要: デフォルト コンストラクターは、カスタム タイプのカスタム タイプのデフォルト コンストラクターを自動的に呼び出します。本質は、デフォルト コンストラクターのデフォルトの初期化リストがカスタム タイプのデフォルト コンストラクターを呼び出すことです。コンストラクターがデフォルトの初期化リストを使用している限り、デフォルトのコンストラクターはカスタム型で呼び出されます。逆に、初期化リストを手動で追加すると、カスタム型のコンストラクターを選択的に呼び出すことができます。
3. 初期化リストによって解決される 3 つの主要な問題
(1) クラス内のメンバ変数を参照する
参照は定義時に初期化する必要があり、そうでないとコンパイルエラーが発生します 詳細はRebirth:翌日には C++ を学びたい_Promise Taizu のブログ - CSDN Blog を参照してください。
クラス内のメンバーはすべて初期化リストで定義されます。メンバーに参照型がある場合は、初期化リストで初期化する必要があります。
#include<iostream>
using namespace std;
class A
{
public :
A(int a)
{
_a = a;//初始化
}
private:
int& _a;
};
int main()
{
int tmp = 2;
A a(tmp);
return 0;
}
このとき、デフォルトの初期化リストでは参照_aの定義が完了しており、コンストラクタ関数本体での代入初期化はコンパイルエラー(定義時に初期化されていない)となります。
解決策: コンストラクターに初期化リストを手動で追加し、初期化リストで定義と初期化を一緒に行います。
A(int a)
:
_a(a)
{
}
(2) constメンバ変数
Const メンバー変数は問題が発生しやすく、参照の原則は同じです。すべての定義を初期化する必要があります。
(3) カスタム型のメンバーにデフォルトのコンストラクターがない場合の問題
コンストラクターが初期化リストを書き込まない場合、デフォルトの初期化リストはカスタム型のデフォルトのコンストラクターを呼び出すことがわかっています。
デフォルトのコンストラクター:
詳しくは「転生四日目にC++を学びたい - プログラマ募集」をご覧ください。
ただし、現時点でこのカスタム型にデフォルトのコンストラクターがない場合、コンパイル エラーが発生します。
現時点では、パラメーター リストを手動で追加し、カスタム タイプのコンストラクターを選択的に呼び出す必要があります。
演算子のオーバーロードの適用
4日目は演算子のオーバーロードについて学習しましたが、演算子のオーバーロードの魅力を感じてもらうために、2つの例を紹介します。
(1) シーケンステーブルにおける演算子の多重定義
まずは簡単なシーケンステーブルを書きます
#include<iostream>
using namespace std;
class Sequence
{
public:
Sequence()
{
//初始化
_a = (int*)malloc(4 * sizeof(int));
_size = 0;
_capcity = 4;
}
void Push(int x)
{
//检查容量:此处忽略
_a[_size] = x;
_size++;
}
void Print()
{
for (int i = 0; i < _size; i++)
{
cout << _a[i] << " ";
}
}
private:
int* _a;
int _size;
int _capcity;
};
int main()
{
Sequence s;//创建顺序表
s.Push(1);//尾插三个数据
s.Push(2);
s.Push(3);
//打印顺序表
s.Print();
return 0;
}
クラス内で [ ] をオーバーロードできます
int& operator[](int i)
{
return _a[i];
}
この時点で、シーケンス テーブル内の要素に配列のようにアクセスできるようになります。
int main()
{
Sequence s;//创建顺序表
s.Push(1);//尾插三个数据
s.Push(2);
s.Push(3);
//打印顺序表
cout << s[0]<<s[1]<<s[2]<<endl;
return 0;
}
[ ] をオーバーロードすると、シーケンス テーブルがより鮮明になり、コードの可読性が大幅に向上します。
(2) カスタムタイプのストリーム挿入とストリーム抽出
Date クラスを例に挙げます。
#include<iostream>
using namespace std;
class Date//类Date
{
public:
Date(int year=1, int month=1, int day=1)//构造函数
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "年" << _month << "月" << _day << "天" << endl;
}
private:
//成员变量的声明
int _year;//内置类型成员
int _month;
int _day;
};
int main()
{
Date d1(1,1,2);
d1.Print();
return 0;
}
印刷機能を使用して日付を印刷できます
しかし、この方法で日付も印刷できたら、目に楽しいでしょうか?
cout<<d1<<endl;
以下では、このアイデアを実現するために演算子のオーバーロードを使用します。
#include<iostream>
using namespace std;
class Date//类Date
{
public:
friend ostream& operator<<(ostream& out, Date& d);//将此函数声明为类Date的友元函数
Date(int year=1, int month=1, int day=1)//构造函数
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "年" << _month << "月" << _day << "天" << endl;
}
private:
//成员变量的声明
int _year;//内置类型成员
int _month;
int _day;
};
ostream& operator<<(ostream& out, Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "天";
return out;
}
int main()
{
Date d1(1, 1, 2);
cout << d1;
return 0;
}
操作結果:
ここで、cout は ostream クラスのオブジェクトであり、演算子オーバーロードをグローバル関数として記述することで、cout オブジェクトと d オブジェクトの位置を入れ替えることができます。フレンド関数を使用すると、クラス外の関数がクラスのプライベート メンバーにアクセスできるようになります。次回はフレンド機能について紹介します。
今日の共有はここまでです。もし皆様のお役に立てれば、プログラマーの方々に 3 回サポートしていただき、引き続き知識を共有していただければ幸いです。ありがとう!