技術共有

C の基本 (2)

2024-07-12

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

目次

1. クラスとオブジェクト

1.1 クラス定義

1.2 アクセス修飾子

1.3 クラスドメイン

2. インスタンス化

2.1 インスタンス化の概念

2.2 オブジェクトのサイズ

3.このポインタ

4.クラスのデフォルトメンバ関数

4.1コンストラクター

4.2 デストラクタ

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


1. クラスとオブジェクト

1.1 クラス定義

クラス定義形式

class はクラスを定義するキーワード、Stack はクラスの名前、{} はクラスの本体です。定義の最後のセミコロンは省略されないことに注意してください。クラス本体の内容はクラスのメンバーと呼ばれます。クラス内の変数はクラスの属性またはメンバー変数と呼ばれ、クラス内の関数はクラスのメソッドまたはメンバー関数と呼ばれます。

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //成员变量
  7. int* a;
  8. int top;
  9. int capacity;
  10. //成员函数
  11. void Push()
  12. {
  13. }
  14. void Pop()
  15. {
  16. }
  17. };//分号不能省略
  18. int main()
  19. {
  20. return 0;
  21. }

  • メンバー変数を区別するために、メンバー変数の前後に _ または m_ で始まるなど、特別な識別子をメンバー変数に追加するのが一般的です。この C++ 構文には規制はなく、個人または会社の好みの問題です。
  1. //为区分成员变量,一般前面加_
  2. //成员变量
  3. int* _a;
  4. int _top;
  5. int _capacity;
  • C++ の Struct はクラスを定義することもできます。C++ は C の struct の使用法と互換性があります。同時に、明らかな変更点は、通常の状況では関数も struct で定義できることです。クラスを定義するにはクラスを使用することをお勧めします。

  • クラスで定義されたメンバーはデフォルトでインラインになります

1.2 アクセス修飾子

C++ はカプセル化を実装する方法で、クラスを使用してオブジェクトのプロパティとメソッドを組み合わせてオブジェクトをより完全にし、アクセス権を通じてそのインターフェイスを外部ユーザーに選択的に提供します。

  • public (public) で変更されたメンバーはクラス外から直接アクセスできますが、 protected (protected) と private (private) で変更されたメンバーはクラス外から直接アクセスできません。 protected と private は同じです
  • アクセス許可のスコープは、アクセス許可が出現する位置から次のアクセス修飾子が出現するまで始まり、後続のアクセス修飾子がない場合、スコープはクラスの終わりである } で終了します。
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. ///
  7. void Push()
  8. {
  9. }
  10. //Push 没给限定符 class默认私有 private
  11. ///
  12. public:
  13. void Pop()
  14. {
  15. }
  16. int Swap()
  17. {
  18. }
  19. //Pop和Swap 被public修饰,直到下一个限定符出现之前都为公有
  20. ///
  21. protected:
  22. int add();
  23. //add 被public修饰,直到下一个限定符出现之前都为保护
  24. /// /
  25. private:
  26. int* _a;
  27. int _top;
  28. int _capacity;
  29. //成员变量被private修饰,直到}结束都为私有
  30. };
  31. int main()
  32. {
  33. Stack st;
  34. //公有可以访问
  35. st.Pop();
  36. st.Swap();
  37. //私有不可访问
  38. st._top;
  39. return 0;
  40. }

その他の注意事項:

  1. クラス定義メンバーがアクセス修飾子によって変更されていない場合、それらはデフォルトでプライベートに設定され、構造体はデフォルトでパブリックに設定されます。
  2. 一般に、メンバー変数はプライベート/プロテクトに制限され、他のユーザーが使用する必要があるメンバー関数はパブリックとして配置されます。

1.3 クラスドメイン

クラスは新しいスコープを定義します。クラスのすべてのメンバーはクラスのスコープ内にあります。クラス外のメンバーを定義する場合は、メンバーがどのクラス スコープに属するかを示すために使用する必要があります。

クラス ドメインはコンパイルの検索規則に影響します。次のプログラムの Init でクラス ドメイン Stack が指定されていない場合、コンパイラは Init をグローバル関数として扱います。コンパイル中に _top などのメンバーが見つからない場合、コンパイラは Init をグローバル関数として扱います。クラスドメインを使用してそれらを見つけます。

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. public:
  7. void Init(int x, int y);
  8. };
  9. void Stack::Init(int x, int y)
  10. {
  11. _top = x;
  12. _capacity = y;
  13. }
  14. int main()
  15. {
  16. return 0;
  17. }

知らせ:

  1. クラス内の関数の宣言と定義は、クラスの作成後に分離される必要があります。指定しないとアクセスできません。

2. インスタンス化

2.1 インスタンス化の概念

  • 物理メモリ内に型を作成するプロセスは、クラスのインスタンス化と呼ばれます。
  • クラスはオブジェクトの抽象的な記述であり、クラスのメンバー変数を制限します。これらのメンバー変数は、オブジェクトがクラスでインスタンス化されるときに割り当てられるだけです。

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Stack
  5. {
  6. //声明
  7. int* _a;
  8. int _top;
  9. int _capacity;
  10. };
  11. int main()
  12. {
  13. Stack::_top = 2024;
  14. //编译器报错,_top只是声明,并未实例化
  15. return 0;
  16. }
  • クラスは複数のオブジェクトをインスタンス化でき、インスタンス化されたオブジェクトは実際の物理空間を占有し、メンバー変数を格納します。たとえば、クラスからオブジェクトをインスタンス化することは、実際に家を建てるために建築設計図を使用することに似ています。設計図には部屋の数や家のサイズなどが計画されていますが、物理的なものはありません。人が住むには、設計図に従って家を建てて初めて、その家に人が住むことができるのです。同じクラスは設計図に似ており、オープンされるメモリの量をコンパイラに伝えるだけで、インスタンス化されたオブジェクトにのみデータを保存するための物理メモリが割り当てられます。
    1. //text.cpp
    2. #include<iostream>
    3. using namespace std;
    4. class Stack
    5. {
    6. //声明
    7. int* _a;
    8. int _top;
    9. int _capacity;
    10. };
    11. int main()
    12. {
    13. Stack st;
    14. st._top=2024;
    15. //Stack实例化出st,系统已经给st分配内存了,可以存储数据,编译通过
    16. return 0;
    17. }

    2.2 オブジェクトのサイズ

  • クラスオブジェクトにどのようなメンバーが含まれているかを分析しますか?クラスによってインスタンス化された各オブジェクトには独立したデータ空間があるため、オブジェクトにはメンバー変数が含まれている必要があります。では、メンバー関数は含まれるのでしょうか。まず、関数がコンパイルされた後は、オブジェクトに格納できない命令のセクションになります。これらの命令は、オブジェクトを格納する必要がある場合、ポインタとしてのみ使用できます。メンバー関数。オブジェクトにポインタを格納する必要がありますか? Date は 2 つのオブジェクト d1 と d2 をインスタンス化します。di と d2 にはそれぞれ独自のデータを格納するための独立したメンバ変数 _year/_month/_day がありますが、d1 と d2 のメンバ関数 Init には /Print ポインタが含まれます。は同じであり、オブジェクトに格納すると無駄になります。 Date を使用して 100 個のオブジェクトをインスタンス化すると、メンバー関数ポインターが 100 回繰り返し格納されることになり、無駄が多すぎます。実際、関数ポインタはアドレスであり、コンパイラはコンパイルおよびリンク時に関数のアドレスを見つける必要があります。実行時には検出されません。状態は実行時にのみ検出されるため、関数のアドレスを保存する必要があります。

メモリアライメントルール

  • 最初のメンバーは構造体からのアドレス オフセット 0 にあります。
  • 他のメンバー変数は、アライメント数の倍数のアドレスにアライメントされる必要があります。
  • アライメント番号 = コンパイラのデフォルトのアライメント番号とメンバーのサイズの小さい方
  • VS x64 プラットフォームのデフォルトのアライメント数は 4 で、x86 のデフォルトのアライメント数は 8 です。
  • 構造体の合計サイズは次のとおりです: 最大アライメント数 (すべての型変数の最大値と最小のデフォルト アライメント数) の整数倍
  • 構造体がネストされている場合、ネストされた構造体はその最大アライメント数の整数倍にアライメントされ、構造全体のサイズはすべての最大アライメント数 (ネストされた構造体のアライメント数を含む) の整数倍になります。
  1. class A
  2. {
  3. public:
  4. void Print()
  5. {
  6. cout << _ch << endl;
  7. }
  8. private:
  9. char _ch;
  10. int _i;
  11. };
  12. //_ch 是一个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存在_ch
  13. // _i是4个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存储_i
  14. class B
  15. {
  16. public:
  17. void Print()
  18. {
  19. //。。。
  20. }
  21. };
  22. class B
  23. {
  24. };
  25. //B和C里面没有存储任何成员变量,只有一个函数,可成员函数不存对象里面
  26. // 按理来说是0,但是结构体怎么会没大小,为表示对象存在C++对这种规定大小为1,为了占位标识对象存在

3.このポインタ

コンパイラーがコンパイルした後、クラスのメンバー関数は、デフォルトで、このポインターと呼ばれる現在のクラスへのポインターを仮パラメーターの最初の位置に追加します。

たとえば、Date クラスの Init プロトタイプは void Init (Date * const this, int year, int month, int day) です。クラスのメンバー関数のメンバー変数にアクセスする場合、本質は this ポインターを介してアクセスされます。 Init 関数の _year、this-&gt;_year=year など。

プロトタイプ:

  1. class Date
  2. {
  3. void Print()
  4. {
  5. cout << _year << "n" << _month << "n" << _day << endl;
  6. }
  7. void Init( int year, int month,int day)
  8. {
  9. _year = year;
  10. _month = month;
  11. _day = day;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(2024,7,10);
  3. d1.Print();
  4. Date d2;
  5. d2.Init(2024, 7, 9);
  6. d2.Print();

実際のプロトタイプ

  1. class Date
  2. {
  3. void Init(Date* const this,int year, int month,int day)
  4. {
  5. this->_year = year;
  6. this->_month = month;
  7. this->_day = day;
  8. }
  9. void Printf(Date* const this)
  10. {
  11. cout << this->_year << "n" <<this-> _month << "n" << this->_day << endl;
  12. }
  13. private:
  14. int _year;
  15. int _month;
  16. int _day;
  17. };

  1. Date d1;
  2. d1.Init(&d1,2024,7,10);
  3. d1.Print(&d1);
  4. Date d2;
  5. d2.Init(&d2,2024, 7, 9);
  6. d2.Print();

C++ では、このポインタを実パラメータおよび仮パラメータの位置に記述することは許可されていないと規定されています (コンパイル時にコンパイラが処理します)。このポインタは関数本体内で明示的に使用できますが、このポインタは変更できません。このポインタが指すコンテンツは、

このポインタはスタックに保存されます

4.クラスのデフォルトメンバ関数

デフォルトメンバ関数とは、ユーザが明示的に定義せず、コンパイラによって自動的に生成されるメンバ関数のことをデフォルトメンバ関数といいます。

4.1コンストラクター

コンストラクターは特別なメンバー関数です。コンストラクターはコンストラクターと呼ばれますが、コンストラクターの主な内容は、オブジェクトを作成するためのスペースを開くことではないことに注意してください (通常使用するローカル オブジェクトは、オブジェクトを作成するときに開かれるスペースです)。スタック フレームは作成されます))、ただし、オブジェクトはインスタンス化時に初期化されます。コンストラクターの本質は、以前に Stack クラスと Date クラスで作成した Init 関数の関数を置き換えることです。コンストラクターの自動呼び出しは、Init 関数を完全に置き換えます。

コンストラクターの特徴:

  1. 関数名はクラス名と同じです
  2. 戻り値なし(戻り値には何も与える必要はありません。void は記述しないでください。これは C++ の規則です)
  3. オブジェクトがインスタンス化されると、システムは対応するコンストラクターを自動的に呼び出します。
  4. コンストラクターはオーバーロードできる
  5. クラスに明示的なコンストラクターが定義されていない場合、C++ コンパイラーはパラメーターのないデフォルト コンストラクターを自動的に生成します。ユーザーがコンストラクターを明示的に定義すると、コンパイラーはそれを生成しなくなります。

  1. class Date
  2. {public:
  3. //1.无参构造函数
  4. Date()
  5. {
  6. _year = 1;
  7. _month = 1;
  8. _day = 1;
  9. }
  10. //2.带参构造函数
  11. Date(int year, int month, int day)
  12. {
  13. _year = year;
  14. _month = month;
  15. _day = day;
  16. }
  17. //3.全缺省构造函数
  18. Date(int year = 1, int month = 1, int day = 1)
  19. {
  20. _year = year;
  21. _month = month;
  22. _day = day;
  23. }
  24. private:
  25. int _year;
  26. int _month;
  27. int _day;
  28. };

パラメーターなしのコンストラクター、すべてデフォルトのコンストラクター、およびコンストラクターを作成しないときにデフォルトでコンパイラーによって生成されるコンストラクターはすべてデフォルト コンストラクターと呼ばれます。ただし、これら 3 つのうち 1 つだけが存在でき、同時に存在することはできません。パラメーターなしのコンストラクターと完全なデフォルトのコンストラクターは関数のオーバーロードを構成しますが、それらを呼び出すときにあいまいさが生じます。デフォルト コンストラクターは、デフォルトでコンパイラーによって生成されるコンストラクターであるだけでなく、パラメーターのないコンストラクターであり、完全なデフォルト コンストラクターも、要約すると、パラメーターを渡さずに呼び出すことができることに注意してください。

デフォルトでは、コンパイラーによって生成される構成体には、組み込み型メンバー変数の初期化の要件はありません。つまり、初期化されるかどうかはコンパイラーに依存します。

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. private:
  19. STDataType* _a;
  20. size_t _capacity;
  21. size_t _top;
  22. };
  23. //两个Stack实现队列
  24. class MyQueue
  25. {
  26. private:
  27. int size;
  28. Stack pushst;
  29. Stack popst;
  30. };
  31. int main()
  32. {
  33. MyQueue my;
  34. return 0;
  35. }

C++ では、型をカスタム型と組み込み型 (基本型) に分類します。組み込み型は、int/char/double/pointer など、言語によって提供されるネイティブ データ型です。カスタム型は、class/struct などのキーワードを使用して独自に定義する型です。ここのコンストラクターは自動的に初期化され、VS は組み込み型のサイズも初期化します。コンパイラーごとに初期化値が異なり、C++ ではそれらの値を指定しません。

カスタム型のメンバー変数の場合、このメンバー変数のデフォルトのコンストラクターを呼び出して初期化する必要があります。このメンバー変数にデフォルトのコンストラクターがない場合、このメンバー変数を初期化する場合は、初期化リストを使用して解決する必要があります。

概要: ほとんどの場合、コンストラクターを自分で実装する必要があります。いくつかのケースでは、MyQueue に似ており、スタックにデフォルトのコンストラクターがある場合、MyQueue を自動的に生成して使用できます。

4.2 デストラクタ

  1. ~Stack()
  2. {
  3. free(_a);
  4. _a = nullptr;
  5. _top = _capacity = 0;
  6. }

デストラクタの特徴:

1. デストラクタ名の前に文字~が付きます。

2. パラメーターも戻り値もありません (コンストラクターと一致します)。

3. クラスにはデストラクターを 1 つだけ含めることができます。定義が表示されない場合、システムはデフォルトのデストラクターを自動的に生成します。

4. オブジェクト宣言サイクルが終了すると、システムは自動的にデストラクターを呼び出します。

5. コンストラクターと同様に、コンパイラーによって自動的に生成されるデストラクターを作成しません。また、カスタム型メンバーが他のデストラクターを呼び出す組み込み型メンバーを処理しません。

6. デストラクターを表示すると、カスタム型メンバーのデストラクターも呼び出されることにも注意してください。つまり、カスタム型メンバーのデストラクターは、状況に関係なく自動的に呼び出されます。

  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. typedef int STDataType;
  5. class Stack
  6. {
  7. public:
  8. Stack(int n = 4)
  9. {
  10. _a = (STDataType*)malloc(sizeof(STDataType) * n);
  11. if (nullptr == _a)
  12. {
  13. perror("malloc申请失败");
  14. }
  15. _capacity = n;
  16. _top = 0;
  17. }
  18. ~Stack()
  19. {
  20. free(_a);
  21. _a = nullptr;
  22. _top=_capacity=0;
  23. }
  24. private:
  25. STDataType* _a;
  26. size_t _capacity;
  27. size_t _top;
  28. };
  29. //两个Stack实现队列
  30. class MyQueue
  31. {public:
  32. //编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
  33. //编译器默认生成MyQueue的析构函数调用了Stack的析构,释放了Stack内部的资源
  34. //显示写析构也会调用Stack的析构
  35. ~MyQueue()
  36. {
  37. cout << "~MyQueue" << endl;
  38. }
  39. private:
  40. Stack pushst;
  41. Stack popst;
  42. };
  43. int main()
  44. {
  45. MyQueue my;
  46. return 0;
  47. }

MyQueue のデストラクターは何も行いませんが、C++ では、メモリを解放するために他のデストラクターが呼び出されることが規定されています。

リソースが要求されない場合、デストラクターを記述する必要はありません。コンパイラーによって生成されたデフォルトのデストラクター (Date など) を直接使用できます。生成されたデフォルトのデストラクターが使用できる場合は、デストラクターを明示的に記述する必要はありません。 MyQueueなどのリソースアプリケーションがありますが、破棄する場合は必ずデストラクタを直接記述してください。そうしないとスタックなどのリソースリークが発生します。

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

  • 型付きオブジェクトで演算子を使用する場合、C++ 言語では演算子のオーバーロードの形式で新しい意味を指定できます。 C++ では、クラス型オブジェクトが演算子を使用する場合、対応する演算子オーバーロードへの呼び出しに変換する必要があると規定しています。変換しない場合、コンパイラはエラーを報告します。
  • 演算子のオーバーロードは、演算子と後で定義する演算子で構成される特定の名前を持つ関数です。他の関数と同様に、戻り値の型とパラメーター リスト、および関数本体もあります。
  1. bool operator<(Date d1, Date d2)
  2. {
  3. }
  4. bool operator==(Date d1,Date d2)
  5. {
  6. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  7. }
  • オーバーロードされた演算子関数は、演算子が作用するパラメーターと同じ数のパラメーターを受け取ります。単項トランスポート コンフォートには 1 つのパラメーターがあり、二項演算子には 2 つのパラメーターがあり、二項演算子の左オペランドが最初のパラメーターに渡され、右オペランドが 2 番目のパラメーターに渡されます。
  1. //text.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Date
  5. {
  6. public:
  7. Date(int year, int month, int day)
  8. {
  9. _year= year;
  10. _month = month;
  11. _day = day;
  12. }
  13. int _year;
  14. int _month;
  15. int _day;
  16. };
  17. bool operator<(Date d1, Date d2)
  18. {
  19. }
  20. bool operator==(Date d1,Date d2)
  21. {
  22. return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
  23. }
  24. int main()
  25. {
  26. Date d1(2024, 7, 10);
  27. Date d2(2024,7,9);
  28. //两种用法都可以
  29. d1 == d2;
  30. operator==(d1 , d2);
  31. return 0;
  32. }
  • オーバーロードされた演算子関数がメンバー関数である場合、その最初のオペランドはデフォルトで暗黙の this ポインターに渡されます。そのため、演算子がメンバー関数としてオーバーロードされると、オペランドより 1 つ少ないパラメーターが与えられます。
  • 演算子がオーバーロードされた後も、その優先順位と結合性は組み込み型の演算と一致したままになります。
  • 構文に存在しない一致を連結して性的演算子を作成することはできません (例:operator@)。