Effective C++ 18-23

3570 ワード

18.クラスのインタフェースを完全かつ最小にする.
クラスのユーザインタフェースとは,このクラスのプログラマーがアクセスできるインタフェースであり,典型的なインタフェースには関数のみが存在し,クラスのデータメンバーをカプセル化する.
完全とは、インタフェースにすべての合理的な操作が含まれている関数です.最小とは、関数ができるだけ少なく、機能が重複しないことを意味します.
インタフェースの関数が少ない原因:インタフェースの関数が多いほど、他の人に理解しにくくなり、関数が多いと混同されます.関数が多くなるとメンテナンスが難しくなり、メンテナンスとアップグレードが難しくなります.長いクラス定義は、長いヘッダファイルをもたらし、コンパイル時間を無駄にします.
19.メンバー関数、非メンバー関数、および友元関数を明確にする
メンバー関数は、メンバー関数ではなく動的バインドを実現する虚関数であってもよい.
クラスのオペレータの再ロードについて考える
operator+が加算の重荷重を行う場合、有理数Rationalクラスにメンバー関数+法の操作があると仮定すると、次の3つの演算について次のようになります.
	result = R1 + R2;
	result = R1 + 2;
	result = 2 + R1;
は、Rationalのコンストラクション関数の宣言がRational(int numerator=0、int denominator=1)であることを前提としています.第1行に対する演算は、R 1である.operator+(R 2)は、R 1のメンバー関数を呼び出すが、2行目の演算についても正しい、ここでは2暗黙変換をRationalクラス(コンパイラは、関数の各パラメータに対して暗黙型変換を行う)と呼び、explicitを宣言しない場合、この行コンパイラは、このように解釈するR 1である.operator+( Rational(2)).コンパイラはこの行の操作をこのように理解しているため、3行目の演算はエラーである.operator+(R 1)では、整数2に対して対応するoperator+関数が見つからない場合、ここでは2を有理数に暗黙的に変換することはありません.コンパイラはまた非メンバーのグローバルなoperator+(2,R 1)を探しに行ったが,結果も見つからず,最後に検索に失敗した.
一方,operator+を友元関数とすると,3行目についてはoperator+(2,R 1)をこのように理解し,2を関数のパラメータとして,コンパイラは暗黙的に有理数クラスに変換しようと試みる.ただし、できるだけ友元関数ではなく、グローバル関数を使用して、クラス内の共有インタフェースを呼び出してオペレータ演算を実現することが望ましい.友元関数の使用は多くの面倒をもたらすため、簡単に言えば、クラスはカプセル化のためであり、友元はクラスのカプセル化性を低下させる.
20.publicインタフェースにデータメンバーが表示されないようにします.
簡単に言えば、publicには関数しかなく、データメンバーの読み書きは関数のみで実現され、このように、関数を設定することでデータメンバーの読み取り不能、読み取り専用、書き込み専用、読み取り可能、書き込み可能を実現することができる.
複雑な点では、機能分離functional abstractionを実現し、関数を使用してデータメンバーへのアクセスを実現する場合、データメンバーの設定を変更するときに、これらの関数にいくつかの変更を行い、コードを追加すれば、効果を実現し、ユーザーにこれらの詳細を隠すことができます.
21.できるだけconstを使用します.
constは、宣言定数に加えて、クラスでは、実パラメータを定数、すなわち宣言パラメータをconstとし、関数がクラス内のメンバーの値を変更しない、すなわち宣言関数をconstとし、関数の戻り値を定数とするためによく使用されます.
関数の戻り値を定数にし、ユーザーエラーの確率を減らします.operator+のリロードのように,戻り値が定数でない場合,この式(a+b)=cに対しては,このようなつまらない誤りを阻止しやすい.
宣言関数はconstであり、実際には関数の再ロードを指し、constオブジェクトに呼び出されますが、1つの関数がconstのバージョンのみである場合、通常オブジェクトをconstオブジェクトに変換する操作が発生します.const関数ではconstのオブジェクトに対する操作では、constオブジェクトの一般データメンバーの値、すなわちconst関数ではオブジェクトのメンバーの値を変更できません.しかしconst関数ではデータメンバーを変更しないのは絶対ではありません.ポインタのあるデータメンバーであれば、関数ではポインタを変更しませんが、ポインタが指す内容の値を変更することができます.このような関数もコンパイラの検出によって検出できますが、constの定義とは異なります.もう1つのconceptual constnessの観点では、constメンバー関数は、ユーザーが気づかないうちにキーワードmutableを使用するオブジェクトのいくつかのデータを変更することができると考えられています.あるいはconst_を使用して強制することもできます.castはローカルのthisポインタを宣言し、このポインタが指す内容の値を強制的に変更します.これは通過できます.
22.できるだけ引用を伝え、値を伝えないでください.
伝達値の場合、関数のパラメータは実パラメータのコピーによって初期化され、関数の呼び出し者は関数の戻り値のコピーである.値によってオブジェクトが渡されます.具体的な意味は、このオブジェクトのクラスのコピーコンストラクション関数によって定義されます.このように空間を浪費し、時間を浪費する.
リファレンスを使用すると、新しいオブジェクトが作成されず、常にリファレンスが渡され、コンストラクション関数とコンストラクション関数の呼び出しはありません.
リファレンスを伝達するもう一つの利点は、切断問題slicing problemを回避することである.派生クラスのオブジェクトがベースクラスのオブジェクトとして渡されると、派生クラスオブジェクトはベースクラスのコンストラクション関数によって派生クラスオブジェクトから変換されたベースクラスオブジェクトをパラメータとして新しい完全なベースクラスオブジェクトに割り当てられ、その派生クラスに含まれる動作特性は切断され、単純なベースクラスオブジェクトにすぎません.伝達参照を使用すると、伝達関数で使用されるのは依然としてこのオブジェクト自体であり、このオブジェクトには多態があり、派生クラスの機能がある.
伝達参照について.一般的にはポインタを使用して実装されますが、intなどの小さなオブジェクトでは、リファレンスよりも値の伝達が効率的です.
23.オブジェクトを返さなければならない場合は、リファレンスを返さないでください.
リファレンスを転送する重大なエラーで、存在しないオブジェクトのリファレンスを転送します.たとえば、オペレータのリロード中のoperator=の場合、その戻り値はconst Tであり、オブジェクトを返す理由:
参照として返されると、この別名の元の名前は誰ですか.どこですか.実装+では、返されるオブジェクトは2つのパラメータではなく、関数に新しく作成されたオブジェクトです.このオブジェクトは、関数が終了すると解放されるため、スタック内に存在しません.このエイリアスは、解放されたメモリを指します.これは正しくありません.このオブジェクトはスタックにも存在せず、+を呼び出した後、このエイリアスが格納されず、このエイリアスが存在するメモリが解放されないため、オブジェクトは動的に確立されません.これはメモリ漏洩です.したがって、参照を返すことはできません.したがって、呼び出し先に戻って関数で一度構造と解析を行い、呼び出しで一度構造を行い、使用後に一度構造関数を呼び出す必要があります.費用がかからなくても、戻りオブジェクトしか使用できません.