Effective C++読書ノート1
8355 ワード
一.C++に慣れる
条項01:C++を一つの言語連邦と見なす
C++をよりよく理解するために,C++を4つの主要な二次言語:Cに分解した.あくまでC++はCをベースにしています.ブロック、文、プリプロセッサ、内蔵データ型、配列、ポインタはすべてCから来ます.プログラミングの効率が悪い.Object-Oreinted C++.この部分は,オブジェクト向け設計の古典的ルールがC++上で最も直接的に実施されている.クラス、カプセル化、継承、マルチステート、virtual関数など...Template C++.これはC++汎用プログラミング部分です.
この4つのセカンダリ言語は、あるセカンダリ言語から別の言語に切り替えると、効率的なプログラミング規則がポリシーを変更する必要があることを覚えておいてください.C++効率的なプログラミングルールは、C++のどの部分を使用するかによって状況によって異なります.
条項02:できるだけconst,enum,inlineで#defineを置き換える
この条項は、「プリプロセッサをコンパイラに置き換えるよりも」に変更できるかもしれません.すなわち、前処理をできるだけ少なくする.コンパイルプロセス:.cファイル-前処理->.iファイル--コンパイル-->.oファイル--リンク->binファイル
前処理プロセスはソースコードをスキャンし、初歩的な変換を行い、新しいソースコードをコンパイラに提供します.前処理命令を含む文とマクロ定義をチェックし、すべての#defineを削除し、すべてのマクロ定義を展開します.前処理プロセスでは、プログラム内のコメントと余分な空白文字も削除されます.前処理プロセスがコンパイラより先にソースコードを処理していることがわかります.前処理命令は、#番号で始まるコード行です.プリコンパイル命令の場所に含まれるファイルを挿入
定数置換#define 2点注意:1.定数ポインタの定義:(定数ポインタは通常ヘッダファイルに格納されるため、ポインタをconstとして宣言する必要がある)const char*const authorName="Shenzi";//ポインタ変数も指向内容も変わらない.const std::string authorName(“Shenzi”); 2.クラス固有定数:(定数の役割ドメインをclass内に制限するには、classのメンバーにする必要があります.定数が最大1つのエンティティのみであることを確認するには、staticメンバーにする必要があります)classA{private:static const int NumTurns=5;//static静的定数のすべてのオブジェクトに1つのコピーしかありません.ここでは、宣言}に初期値が与えられています.後の定義では、初期値を設定する必要はありません.In-class初期値設定は、整数定数のみを許可します.文法に加えて、ヘッダファイルクラス内で宣言し、実装ファイル内でクラス外で定義して初期値を付与することもできます.もしコンパイラが「static整数型class定数」で「in class初期値設定」(クラスの宣言で静的整形の初期値を設定することを許可しない場合)、列挙タイプによって補償することができます(列挙タイプに属する数値はintsが使用されることを許可します):enum{NumTurns=5};*constのアドレスを取るのは合法ですが、enumのアドレスを取るのは合法ではありません.defineのアドレスを取るのは通常合法ではありません.pointerやreferenceが整数定数を指すようにしたくない場合は、enumはこの制約を実現するのに役立ちます.
単純定数の場合は、#definesをconstオブジェクトまたはenumsに置き換えることが望ましいことを覚えておいてください.近似関数のマクロについては、definesをinline関数に置き換えることが望ましい.
条項03:できるだけconstを使用する
constでは、コンパイラや他のプログラマーに値を変更すべきではないことを伝えることができます.「値」が変更されるべきではない限り、それは確かに言うべきです.キーワードconst多才多芸:例:char greeting[]=「Hello」;char *p = greeting;//ポインタpおよび指す文字列はいずれも変更可能である.const char *p = greeting;//ポインタp自体は、p=&Anyotherのように変更することができる.pが指す文字列は変更できない.char * cosnt p = greeting;//ポインタpは変更できず、指すオブジェクトは変更可能である.const char * const p = greeting;//ポインタpおよびそれによるオブジェクトは変更できない.
const用法説明:キーワードconstがアスタリスクの左側に表示される場合、被指物が定数であることを示します.const char*pとchar const*pの2つの書き方の意味は同じで、いずれも対象が定数であることを説明している.キーワードconstがアスタリスクの右側に表示されている場合は、ポインタ自体が定数であることを示します.星の両側に現れると、被指物とポインタの両方が定数であることを示します.STL(標準テンプレートライブラリ)の例では、反復器の役割はT*ポインタのようなものであり、反復器を定数と宣言することは、宣言ポインタ自体がconstであることと同じである.
反復器について:反復器はポインタのようなもので、容器の中の要素にアクセスできますが、他の操作もできます.STLは各コンテナに反復器を定義し,テンプレートを用いることで,関数はコンテナに格納されたデータ型だけでなく,コンテナ自体のデータ構造からも独立することができる.反復器には5種類あり、それに応じて異なる機能があります.
constは、関数の先頭に定数値を返すために使用され、セキュリティと効率性を放棄することなく、お客様のエラーによる予期せぬ事故を低減できます.例:const Rational operator*(const Rational&lhs,cosnt Rational&rhs);リファレンスは戻り値として、(1)ローカル変数のリファレンスを返すことができないというルールに従う必要があります.主な理由は,局所変数が関数が返された後に破棄されるため,返された参照が「指すものがない」参照となり,プログラムが未知の状態になるためである.(2)関数内部newで割り当てられたメモリの参照を返すことができない.局所変数のパッシブ破棄の問題は存在しないが,この場合(関数内部のnew割り当てメモリの参照を返す)には,他の気まずい状況に直面する.例えば、関数によって返される参照は一時変数として現れるだけで、実際の変数が与えられていないと、この参照が指す空間(newによって割り当てられる)は解放されず、memory leakとなる.(3)クラスメンバーの参照を返すことができますが、constが望ましいです.主な理由は、オブジェクトのプロパティがビジネス・ルールに関連付けられている場合、その付与は他のプロパティまたはオブジェクトのステータスに関連することが多いため、付与操作をビジネス・ルールにカプセル化する必要があるからです.他のオブジェクトが属性の非常に多くの参照(またはポインタ)を取得できる場合、属性の単純な付与はビジネス・ルールの完全性を破壊します.constメンバー関数の例const char&operator[](std:size_t position)const constメンバー関数はclassインタフェースを理解しやすくし、「constオブジェクトの操作」を可能と呼ぶ.説明:constとして宣言されたメンバー関数(関数名にconstを付ける)は、non-staticメンバー変数を変更することはできません.メンバー変数宣言の前にmutable(可変)を追加すると、constメンバー関数で変更できます.
コンパイラが誤った使い方を検出するのに役立つものがあることを覚えておいてください.constは、任意の役割ドメイン内のオブジェクト、関数パラメータ、関数戻りタイプ、メンバー関数本体に適用することができる.コンパイラはbitwise constnessを強制的に実施しますが、プログラムを書くときは「コンセプト上の車両」(conceptual constness)を使用する必要があります.cosntとnon-constメンバー関数が実質的に等価に実装されている場合、non-constバージョンにconstバージョンを呼び出すと、コードの重複を回避できます.ただしconstメンバー関数でnon-constメンバー関数を呼び出すことはできません
条項04:オブジェクトが使用される前に初期化されていることを確認する
割り当てと初期化の区別:C++は,オブジェクトのメンバ変数の初期化動作がコンストラクション関数本体に入る前に発生することを規定する.したがって、メンバー変数の初期化は、コンストラクション関数の初期化リストに配置されます.ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list& phones) {theName=name;//これらは、theAddress=addressを初期化するのではなく、割り当てられた値です.//これらのメンバー変数は、関数体に入る前にデフォルトのコンストラクション関数を呼び出し、次いで、thePhones=phones;//つまり、2回の関数呼び出しを行います.numTimesConsulted=0;}
例:非パラメトリックコンストラクション関数ABEntry::ABEntry():theName()//thenameのデフォルトコンストラクション関数theAddress()//thePhones()、numTimesConsulted(0){}を呼び出す
メンバー変数がconstまたはreferenceの場合、初期値が必要であり、値を割り当てることはできません.条項5を参照して面倒を避けるには、メンバー初期値列C++を常に使用することで、非常に固定された「メンバー初期化順序」を持つことが簡単です.ベースクラスは常に派生クラスの前に初期化され、クラスのメンバー変数は常にその説明順に初期化されます.したがって、メンバー初期化リストに各メンバーがリストされている場合は、常にその宣言順にすることが望ましい.non-local staticオブジェクトの初期化順序を議論するstaticオブジェクトとは、globalオブジェクト、namespace役割ドメイン内に定義されたオブジェクト、class内、関数内、およびfile役割ドメイン内でstaticと宣言されたオブジェクトを含む.関数内のstaticオブジェクトをlocal staticオブジェクト、その他のstaticオブジェクトをnon-local staticオブジェクトと呼び、プログラム終了時にstaticオブジェクトが自動的に破棄されます.「コンパイルユニットにまたがるnon-local static初期化順序」では、non-local staticオブジェクトの初期化動作は、別のコンパイラユニット内のnon-local staticオブジェクトを使用します.このオブジェクトはまだ初期化されていない可能性があります.解決策は、non-local staticオブジェクトごとに独自の専用関数に移動することです.これらの関数は、referenceが含むオブジェクトを指します.
C++は初期化を保証しないため、組み込みオブジェクトを手動で初期化することを覚えておいてください.コンストラクション関数は、コンストラクション関数本体で付与操作を使用するのではなく、メンバー初期化リストを使用することが望ましい.クラス内の宣言順序と同じ順序で並べ替えられるメンバー変数を初期化します.コンパイルユニット間の初期化順序の問題を回避するには、non-local staticオブジェクトをlocal staticオブジェクトに置き換えます.
条項01:C++を一つの言語連邦と見なす
C++をよりよく理解するために,C++を4つの主要な二次言語:Cに分解した.あくまでC++はCをベースにしています.ブロック、文、プリプロセッサ、内蔵データ型、配列、ポインタはすべてCから来ます.プログラミングの効率が悪い.Object-Oreinted C++.この部分は,オブジェクト向け設計の古典的ルールがC++上で最も直接的に実施されている.クラス、カプセル化、継承、マルチステート、virtual関数など...Template C++.これはC++汎用プログラミング部分です.
この4つのセカンダリ言語は、あるセカンダリ言語から別の言語に切り替えると、効率的なプログラミング規則がポリシーを変更する必要があることを覚えておいてください.C++効率的なプログラミングルールは、C++のどの部分を使用するかによって状況によって異なります.
条項02:できるだけconst,enum,inlineで#defineを置き換える
この条項は、「プリプロセッサをコンパイラに置き換えるよりも」に変更できるかもしれません.すなわち、前処理をできるだけ少なくする.コンパイルプロセス:.cファイル-前処理->.iファイル--コンパイル-->.oファイル--リンク->binファイル
前処理プロセスはソースコードをスキャンし、初歩的な変換を行い、新しいソースコードをコンパイラに提供します.前処理命令を含む文とマクロ定義をチェックし、すべての#defineを削除し、すべてのマクロ定義を展開します.前処理プロセスでは、プログラム内のコメントと余分な空白文字も削除されます.前処理プロセスがコンパイラより先にソースコードを処理していることがわかります.前処理命令は、#番号で始まるコード行です.プリコンパイル命令の場所に含まれるファイルを挿入
:#define ASPECT_RATIO 1.653
ASPECT_RATIO , 。 ASPECT_RATIO 1.653 。ASPECT_RATIO (symbol table)。 。
:const double AspectRatio = 1.653;
: , #define , 1.653; 。
定数置換#define 2点注意:1.定数ポインタの定義:(定数ポインタは通常ヘッダファイルに格納されるため、ポインタをconstとして宣言する必要がある)const char*const authorName="Shenzi";//ポインタ変数も指向内容も変わらない.const std::string authorName(“Shenzi”); 2.クラス固有定数:(定数の役割ドメインをclass内に制限するには、classのメンバーにする必要があります.定数が最大1つのエンティティのみであることを確認するには、staticメンバーにする必要があります)classA{private:static const int NumTurns=5;//static静的定数のすべてのオブジェクトに1つのコピーしかありません.ここでは、宣言}に初期値が与えられています.後の定義では、初期値を設定する必要はありません.In-class初期値設定は、整数定数のみを許可します.文法に加えて、ヘッダファイルクラス内で宣言し、実装ファイル内でクラス外で定義して初期値を付与することもできます.もしコンパイラが「static整数型class定数」で「in class初期値設定」(クラスの宣言で静的整形の初期値を設定することを許可しない場合)、列挙タイプによって補償することができます(列挙タイプに属する数値はintsが使用されることを許可します):enum{NumTurns=5};*constのアドレスを取るのは合法ですが、enumのアドレスを取るのは合法ではありません.defineのアドレスを取るのは通常合法ではありません.pointerやreferenceが整数定数を指すようにしたくない場合は、enumはこの制約を実現するのに役立ちます.
:#define CALL_WITH_MAX(a,b) f((a) > (b)) ? (a) : (b))
。
, , 。
:
template<typename T>
inline void callWithMax(cosnt T &a, cosnt T &b)
{
f(a > b ? a : b);
}
callWithMax , 。
単純定数の場合は、#definesをconstオブジェクトまたはenumsに置き換えることが望ましいことを覚えておいてください.近似関数のマクロについては、definesをinline関数に置き換えることが望ましい.
条項03:できるだけconstを使用する
constでは、コンパイラや他のプログラマーに値を変更すべきではないことを伝えることができます.「値」が変更されるべきではない限り、それは確かに言うべきです.キーワードconst多才多芸:例:char greeting[]=「Hello」;char *p = greeting;//ポインタpおよび指す文字列はいずれも変更可能である.const char *p = greeting;//ポインタp自体は、p=&Anyotherのように変更することができる.pが指す文字列は変更できない.char * cosnt p = greeting;//ポインタpは変更できず、指すオブジェクトは変更可能である.const char * const p = greeting;//ポインタpおよびそれによるオブジェクトは変更できない.
const用法説明:キーワードconstがアスタリスクの左側に表示される場合、被指物が定数であることを示します.const char*pとchar const*pの2つの書き方の意味は同じで、いずれも対象が定数であることを説明している.キーワードconstがアスタリスクの右側に表示されている場合は、ポインタ自体が定数であることを示します.星の両側に現れると、被指物とポインタの両方が定数であることを示します.STL(標準テンプレートライブラリ)の例では、反復器の役割はT*ポインタのようなものであり、反復器を定数と宣言することは、宣言ポインタ自体がconstであることと同じである.
const std::vector<int>::interator iter = vec.begin(); //iter T *const,
++iter ; // :iter const
*iter = 10 // , iter
std::vector<int>::const_iterator cIter = vec.begin(); // const T*
*cIter = 10 :*cIter const
:std::vector<int>::interator iter iter std::vector<int>
反復器について:反復器はポインタのようなもので、容器の中の要素にアクセスできますが、他の操作もできます.STLは各コンテナに反復器を定義し,テンプレートを用いることで,関数はコンテナに格納されたデータ型だけでなく,コンテナ自体のデータ構造からも独立することができる.反復器には5種類あり、それに応じて異なる機能があります.
constは、関数の先頭に定数値を返すために使用され、セキュリティと効率性を放棄することなく、お客様のエラーによる予期せぬ事故を低減できます.例:const Rational operator*(const Rational&lhs,cosnt Rational&rhs);リファレンスは戻り値として、(1)ローカル変数のリファレンスを返すことができないというルールに従う必要があります.主な理由は,局所変数が関数が返された後に破棄されるため,返された参照が「指すものがない」参照となり,プログラムが未知の状態になるためである.(2)関数内部newで割り当てられたメモリの参照を返すことができない.局所変数のパッシブ破棄の問題は存在しないが,この場合(関数内部のnew割り当てメモリの参照を返す)には,他の気まずい状況に直面する.例えば、関数によって返される参照は一時変数として現れるだけで、実際の変数が与えられていないと、この参照が指す空間(newによって割り当てられる)は解放されず、memory leakとなる.(3)クラスメンバーの参照を返すことができますが、constが望ましいです.主な理由は、オブジェクトのプロパティがビジネス・ルールに関連付けられている場合、その付与は他のプロパティまたはオブジェクトのステータスに関連することが多いため、付与操作をビジネス・ルールにカプセル化する必要があるからです.他のオブジェクトが属性の非常に多くの参照(またはポインタ)を取得できる場合、属性の単純な付与はビジネス・ルールの完全性を破壊します.constメンバー関数の例const char&operator[](std:size_t position)const constメンバー関数はclassインタフェースを理解しやすくし、「constオブジェクトの操作」を可能と呼ぶ.説明:constとして宣言されたメンバー関数(関数名にconstを付ける)は、non-staticメンバー変数を変更することはできません.メンバー変数宣言の前にmutable(可変)を追加すると、constメンバー関数で変更できます.
const_cast<char &>(static_cast<const TextBlock &>(*this))[position];
//static_cast TextBlock & const TextBlock &;
//const_cast const ;
コンパイラが誤った使い方を検出するのに役立つものがあることを覚えておいてください.constは、任意の役割ドメイン内のオブジェクト、関数パラメータ、関数戻りタイプ、メンバー関数本体に適用することができる.コンパイラはbitwise constnessを強制的に実施しますが、プログラムを書くときは「コンセプト上の車両」(conceptual constness)を使用する必要があります.cosntとnon-constメンバー関数が実質的に等価に実装されている場合、non-constバージョンにconstバージョンを呼び出すと、コードの重複を回避できます.ただしconstメンバー関数でnon-constメンバー関数を呼び出すことはできません
条項04:オブジェクトが使用される前に初期化されていることを確認する
。 * *, 。 , , 。
割り当てと初期化の区別:C++は,オブジェクトのメンバ変数の初期化動作がコンストラクション関数本体に入る前に発生することを規定する.したがって、メンバー変数の初期化は、コンストラクション関数の初期化リストに配置されます.ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list& phones) {theName=name;//これらは、theAddress=addressを初期化するのではなく、割り当てられた値です.//これらのメンバー変数は、関数体に入る前にデフォルトのコンストラクション関数を呼び出し、次いで、thePhones=phones;//つまり、2回の関数呼び出しを行います.numTimesConsulted=0;}
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
: theName(name), //
theAddress(address), // , 。
thePhones(phones),
numTimesConsulted(0)
{ }
, , 。 , numTimesConsulted(int), , 。
例:非パラメトリックコンストラクション関数ABEntry::ABEntry():theName()//thenameのデフォルトコンストラクション関数theAddress()//thePhones()、numTimesConsulted(0){}を呼び出す
メンバー変数がconstまたはreferenceの場合、初期値が必要であり、値を割り当てることはできません.条項5を参照して面倒を避けるには、メンバー初期値列C++を常に使用することで、非常に固定された「メンバー初期化順序」を持つことが簡単です.ベースクラスは常に派生クラスの前に初期化され、クラスのメンバー変数は常にその説明順に初期化されます.したがって、メンバー初期化リストに各メンバーがリストされている場合は、常にその宣言順にすることが望ましい.non-local staticオブジェクトの初期化順序を議論するstaticオブジェクトとは、globalオブジェクト、namespace役割ドメイン内に定義されたオブジェクト、class内、関数内、およびfile役割ドメイン内でstaticと宣言されたオブジェクトを含む.関数内のstaticオブジェクトをlocal staticオブジェクト、その他のstaticオブジェクトをnon-local staticオブジェクトと呼び、プログラム終了時にstaticオブジェクトが自動的に破棄されます.「コンパイルユニットにまたがるnon-local static初期化順序」では、non-local staticオブジェクトの初期化動作は、別のコンパイラユニット内のnon-local staticオブジェクトを使用します.このオブジェクトはまだ初期化されていない可能性があります.解決策は、non-local staticオブジェクトごとに独自の専用関数に移動することです.これらの関数は、referenceが含むオブジェクトを指します.
class filesysterm{};
filesysterm & tfs() // tfs , filesysterm class static
{
static filesysterm fs; // local static reference
return fs;
}
C++は初期化を保証しないため、組み込みオブジェクトを手動で初期化することを覚えておいてください.コンストラクション関数は、コンストラクション関数本体で付与操作を使用するのではなく、メンバー初期化リストを使用することが望ましい.クラス内の宣言順序と同じ順序で並べ替えられるメンバー変数を初期化します.コンパイルユニット間の初期化順序の問題を回避するには、non-local staticオブジェクトをlocal staticオブジェクトに置き換えます.