C++ コア ガイドライン:クラス ルール

クラスは、プログラマーが表現、操作、およびインターフェースを指定できるユーザー定義型です。 C++ コア ガイドラインには、ユーザー定義型に関する多くの規則があります。

ガイドラインは非常に一般的なルールから始まりますが、コンストラクターとデストラクタ、クラス階層、演算子のオーバーロード、共用体に関する特別なルールも含まれています。

もっと興味深い特別なルールについて書く前に、8 つの一般的なルールを次に示します。

  • C.1:関連データを構造に整理する (struct s または class es)
  • C.2:class を使用する クラスに不変式がある場合。 struct を使用 データ メンバーが独立して変化できる場合
  • C.3:クラスを使用してインターフェースと実装の違いを表す
  • C.4:クラスの表現に直接アクセスする必要がある場合は、関数をメンバーのみにする
  • C.5:サポートするクラスと同じ名前空間にヘルパー関数を配置する
  • C.7:クラスまたは列挙型を定義し、同じステートメントでその型の変数を宣言しない
  • C.8:class を使用 struct ではなく 非公開のメンバーがいる場合
  • C.9:メンバーの露出を最小限に抑える

私は彼らの意図を明確にするために、一般的なクラス規則にのみ書き込みます.

クラスの一般規則

データが関連している場合は、それを構造体またはクラスに配置する必要があります。したがって、2 番目の関数は非常に理解しやすいものです。

void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships
void draw(Point from, Point to); // better

C. 2:class を使用 クラスに不変式がある場合。 struct を使用 データ メンバーが独立して変化できる場合

不変条件は、通常、コンストラクターによって確立される論理条件です。

struct Pair { // the members can vary independently
 string name;
 int volume;
};

class Date {
public:
 // validate that {yy, mm, dd} is a valid date and initialize
 Date(int yy, Month mm, char dd);
 // ...
private:
 int y;
 Month m;
 char d; // day
};

クラス Date には、不変条件 y、m、および d があります。それらはコンストラクターで初期化され、チェックされます。データ型 Pair には不変条件がありません。したがって、これは構造体です。

不変であるため、クラスは使いやすくなっています。これがまさに次のルールの目的です。

C.3:インターフェイスとクラスを使用した実装

この場合、公開メソッドはクラスのインターフェースであり、非公開部分は実装です。

class Date {
 // ... some representation ...
public:
 Date();
 // validate that {yy, mm, dd} is a valid date and initialize
 Date(int yy, Month mm, char dd);

 int day() const;
 Month month() const;
 // ...
};

保守性の観点から、クラス Date の実装は、ユーザーに影響を与えることなく変更できます。

C. 4:クラスの表現に直接アクセスする必要がある場合は、関数をメンバーのみにする

関数がクラスの内部にアクセスする必要がない場合、それはメンバーであってはなりません。したがって、疎結合になり、クラスの内部構造を変更しても関数には影響しません。

C.5:ヘルパー関数を彼らがサポートするクラス

このようなヘルパー関数は、クラスの名前空間にある必要があります。

namespace Chrono { // here we keep time-related services

 class Date { /* ... */ };

 // helper functions:
 bool operator==(Date, Date);
 Date next_weekday(Date);
 // ...
}
...
if (date1 == date2){ ... // (1)

引数依存ルックアップ (ADL) のおかげで、(1) の比較はさらに Chrono 名前空間の ID 演算子を探します。

C.7:クラスまたは列挙型を定義して、同じステートメントでその型の変数を宣言しない

確かに、同じステートメントでクラスを定義し、その型の変数を宣言すると混乱します。

// bad
struct Data { /*...*/ } data{ /*...*/ }; 

// good
struct Data { /*...*/ }; 
Data data{ /*...*/ };

C.8:class を使用 struct ではなく 非公開のメンバーがいる場合

これは非常に便利で、頻繁に使用される規則です。データ型にプライベートまたは保護されたメンバーがある場合は、それをクラスにします。

C.9:メンバーの露出を最小限に抑える

この規則はデータ隠蔽とも呼ばれ、オブジェクト指向クラス設計の基礎の 1 つです。これは、クラスの 2 つのインターフェイスについて考える必要があることを意味します。一般的なユース ケースのパブリック インターフェイスと、派生クラスの保護されたインターフェイス。残りのメンバーは非公開にする必要があります。

より特別なルールを続けます。概要は次のとおりです:

  • C.concrete:具体的な型
  • C.ctor:コンストラクタ、代入、およびデストラクタ
  • C.con:コンテナおよびその他のリソース ハンドル
  • C.lambdas:関数オブジェクトとラムダ
  • C.hier:クラス階層 (OOP)
  • C.over:オーバーロードとオーバーロードされた演算子
  • C.union:組合

具体的な型に対する 2 つのルールを続けましょう。

コンクリート タイプ

  • C.10:クラス階層よりも具象型を好む
  • C.11:具象型を規則的にする

まず、具象型と通常型について書かなければなりません。

具体的なタイプ 「最も単純な種類のクラス」です。多くの場合、値型と呼ばれ、型階層の一部ではありません。もちろん、抽象型を具象にすることはできません。

レギュラータイプ 「int のように動作する」型であるため、コピーと代入、等価性、および順序をサポートする必要があります。よりフォーマルに。通常タイプの Regular は、次の操作をサポートします。

  • コピーと割り当て
    Regular a;
    Regular a = b;
    ~Regular(a);
    a = b;
    
    • 平等

    a == b;
    a != b;
    • 注文

    a < b;

    組み込みの型は、標準テンプレート ライブラリのコンテナーなど、通常のものです。

    C.10:クラス階層よりも具象型を優先する

    クラス階層のユース ケースがない場合は、具象型を使用します。具体的な型は、実装がはるかに簡単で、小さく、高速です。メモリの割り当てと解放を含む、継承、仮想性、参照、またはポインターについて考える必要はありません。仮想ディスパッチがないため、実行時のオーバーヘッドがありません。

    あなただけの価値があります。

    C.11:具象型を規則的にする

    通常の型 (int) の方が理解しやすいです。それらはそれ自体直感的です。つまり、具象型がある場合は、通常の型にアップグレードすることを検討してください。

    次のステップ

    次の投稿は、オブジェクトのライフタイム (作成、コピー、移動、破棄) についてです。