Effective STL:雑記(一)
1.vectorの使用を避ける
vectorは実際にはSTLコンテナとは言えず、boolも実際には格納されていません.1つのオブジェクトがSTLコンテナになるには、C++規格の23.1節に記載されているすべての条件を満たす必要があります.1つの条件は、cがオブジェクトTを含むコンテナであり、cがoperator[]をサポートしている場合、次のコードをコンパイルできることです.
すなわち、コンテナに格納されているオブジェクトTであるべきであり、operator[]はコンテナに実際に格納されているオブジェクトを返すことができ、そのアドレスを取ることでオブジェクトを指すポインタを得ることもできます.(ここではTがoperator&を非常に異例の方法でリロードしていないと仮定する必要がある.)しかしvectorでは,下位層はビットマップに類似した方法で格納される.
vectorのoperator[]では、単位への参照のように表現されるプロキシオブジェクト(proxy project)が返されます.このエージェントオブジェクトに暗黙的にboolに転向する関数を追加するとoperator[]にbool値を返すことができます.したがってoperator[]の戻り値に対してoperator&を行うと、得られるのはboolポインタではないので、boolポインタに付与することは明らかにコンパイルできません.
上のコード出力はそれぞれ1と0です.ただし、コメントされた文のコメント記号を削除すると、コンパイルは通過できません.ここでvb[1]と*vb.begin()が正常に動作するのは、戻り値を特殊に処理しているはずです.詳細はソースコードを分析する必要があります.
vectorに代わるdequeとbitsetがあり、dequeは確かにboolを格納している.bitsetはSTLコンテナではなく、標準C++ライブラリの一部であるが、その大きさはコンパイル時に決定される.
2.swapテクニック余分な容量を取り除く
vector要素が増加するにつれて、pop_を呼び出してもvectorの長さが大きくなることを知っています.backは要素をポップアップし、容量も小さくなりません.最終的にはresize操作で容量を減らす必要があるかもしれません.resize操作よりも、2つのvectorの内容をswap関数で交換するより簡単な方法で余分な容量を除去することができます.
上のコードはそれぞれ16と9を出力します.最も主要な文は「vector(vi).swap(vi)」であり、ここでviの内容で一時vector一時変数を初期化すると、この一時変数はviに実際に存在する要素のみを含み、設定されていない容量内容は過去に値されず、すなわちこの一時変数は9つの要素のみを含む.そしてviとコンテンツを交換すると、viには9つの要素しかなく、余分な容量は存在しません.
3.mapにおけるoperater[]とinsert
map::operator[]の設計目的は、「追加と更新」(add or update)の機能を提供することです.operator[]は、kに関連付けられた値オブジェクトを指す参照を返します.その後、vは、参照(operator[]がその参照を返す)が指すオブジェクトに割り当てられる.キーkに先に関連付けられた値がある場合、値は更新されますが、問題は、kがマッピングテーブルにまだ存在しない場合、値タイプのデフォルトコンストラクション関数を使用して新しいオブジェクトが作成され、operator[]が新しいオブジェクトへの参照を返すことです.
すなわち、キーkに対応するvが存在しない場合、operator[]は、新しいvが付与されているかどうかにかかわらず、新しいオブジェクトを作成する.
以上の出力により、m[10]が実行された後、mにオブジェクトが追加されたことがわかる.
一方、operator[]とinsertの場合、「Effective STL」では、新規オブジェクトの場合、insert関数の効率が高く、更新操作はoperator[]の効率が高いと述べています.本の中にはいくつかの例があって、まだ完全に消化していないので、直接分析しません.
上記の例ではoperator[]であるキーに対応する値があるか否かを判断するのではなくfind関数で判断すべきであることも分かる.
vectorは実際にはSTLコンテナとは言えず、boolも実際には格納されていません.1つのオブジェクトがSTLコンテナになるには、C++規格の23.1節に記載されているすべての条件を満たす必要があります.1つの条件は、cがオブジェクトTを含むコンテナであり、cがoperator[]をサポートしている場合、次のコードをコンパイルできることです.
T *p = &c[0];
すなわち、コンテナに格納されているオブジェクトTであるべきであり、operator[]はコンテナに実際に格納されているオブジェクトを返すことができ、そのアドレスを取ることでオブジェクトを指すポインタを得ることもできます.(ここではTがoperator&を非常に異例の方法でリロードしていないと仮定する必要がある.)しかしvectorでは,下位層はビットマップに類似した方法で格納される.
vectorのoperator[]では、単位への参照のように表現されるプロキシオブジェクト(proxy project)が返されます.このエージェントオブジェクトに暗黙的にboolに転向する関数を追加するとoperator[]にbool値を返すことができます.したがってoperator[]の戻り値に対してoperator&を行うと、得られるのはboolポインタではないので、boolポインタに付与することは明らかにコンパイルできません.
#include
#include
上のコード出力はそれぞれ1と0です.ただし、コメントされた文のコメント記号を削除すると、コンパイルは通過できません.ここでvb[1]と*vb.begin()が正常に動作するのは、戻り値を特殊に処理しているはずです.詳細はソースコードを分析する必要があります.
vectorに代わるdequeとbitsetがあり、dequeは確かにboolを格納している.bitsetはSTLコンテナではなく、標準C++ライブラリの一部であるが、その大きさはコンパイル時に決定される.
2.swapテクニック余分な容量を取り除く
vector要素が増加するにつれて、pop_を呼び出してもvectorの長さが大きくなることを知っています.backは要素をポップアップし、容量も小さくなりません.最終的にはresize操作で容量を減らす必要があるかもしれません.resize操作よりも、2つのvectorの内容をswap関数で交換するより簡単な方法で余分な容量を除去することができます.
#include
#include
using namespace std;
int main() {
vector vi;
for (int i = 0; i < 10; ++i)
vi.push_back(i);
vi.pop_back();
cout << vi.capacity() << endl; // 16
vector(vi).swap(vi);
cout << vi.capacity() << endl; // 9
return 0;
}
上のコードはそれぞれ16と9を出力します.最も主要な文は「vector(vi).swap(vi)」であり、ここでviの内容で一時vector一時変数を初期化すると、この一時変数はviに実際に存在する要素のみを含み、設定されていない容量内容は過去に値されず、すなわちこの一時変数は9つの要素のみを含む.そしてviとコンテンツを交換すると、viには9つの要素しかなく、余分な容量は存在しません.
3.mapにおけるoperater[]とinsert
map::operator[]の設計目的は、「追加と更新」(add or update)の機能を提供することです.operator[]は、kに関連付けられた値オブジェクトを指す参照を返します.その後、vは、参照(operator[]がその参照を返す)が指すオブジェクトに割り当てられる.キーkに先に関連付けられた値がある場合、値は更新されますが、問題は、kがマッピングテーブルにまだ存在しない場合、値タイプのデフォルトコンストラクション関数を使用して新しいオブジェクトが作成され、operator[]が新しいオブジェクトへの参照を返すことです.
すなわち、キーkに対応するvが存在しない場合、operator[]は、新しいvが付与されているかどうかにかかわらず、新しいオブジェクトを作成する.
#include
#include
以上の出力により、m[10]が実行された後、mにオブジェクトが追加されたことがわかる.
一方、operator[]とinsertの場合、「Effective STL」では、新規オブジェクトの場合、insert関数の効率が高く、更新操作はoperator[]の効率が高いと述べています.本の中にはいくつかの例があって、まだ完全に消化していないので、直接分析しません.
上記の例ではoperator[]であるキーに対応する値があるか否かを判断するのではなくfind関数で判断すべきであることも分かる.