string strLine;//not constant
int index = 0;
while(index < strLine.length()){//strLine is not modified};
何回strLine.length()
評価されるか
ループの直前に割り当てられたものを使用nLength
する必要がありますかnLength
strLine.length()
string strLine;//not constant
int index = 0;
while(index < strLine.length()){//strLine is not modified};
何回strLine.length()
評価されるか
ループの直前に割り当てられたものを使用nLength
する必要がありますかnLength
strLine.length()
length
はループを経由するたびに評価されますが、length
一定の時間 ( O(1)
) であるため大きな違いはなく、この値を格納するための変数を追加しても、コードの読みやすさにわずかな影響があるだけで、おそらく無視できる程度の影響しかありません (また、文字列が変更された場合にコードを壊します)。
length() は、ソース ファイルに含まれるヘッダーで定義されているため、コンパイラによってインライン化できます。内部呼び出しもインライン化できるため、コンパイラが文字列インスタンスがループ内で変更されていないことを検出できる場合次に、文字列の長さへのアクセスを最適化できるため、一度だけ評価されます。いずれにせよ、文字列の長さの値を保存する必要はないと思います。数ナノ秒節約できるかもしれませんが、コードが大きくなり、ループ内でその文字列を変更することを決定したときにリスクが発生します。
呼び出されるたびに...(評価中)。文字列の長さを変更しない場合は、次のような一時変数を使用することをお勧めします。
string strLine;
int stringLength = strLine.length();
int index = 0;
while(index < stringLength);
これには 2 つ目の質問が潜んでいると思います。それは「どちらの実装がより明確か?」ということです。
意味的に、strLine の長さがループの本体内で変化しないことを意味する場合は、適切な名前の変数に代入することで明確にします。私はそれをconstにさえします。これにより、比較値が決して変化しないことが他のプログラマー (および自分自身) に明らかになります。
これが行うもう 1 つのことは、デバッガーでコードをステップ実行しているときに、その値が何であるかを簡単に確認できるようにすることです。ホバーオーバーは、関数呼び出しよりもローカルでうまく機能します。
「関数呼び出しのままにしておいてください。コンパイラーが最適化します」と言うのは、時期尚早の悲観論のように思えます。length() は O(1) ですが、インライン化されていない場合 (最適化が無効になっていないことを保証できません)、それは重要な関数呼び出しです。ローカル変数を使用することで、意味が明確になり、パフォーマンスが大幅に最適化される可能性があります。
あなたの意図を最も明確にすることをしてください。
一時変数を使用する場合は、const 修飾子を使用して、値が変更されないことをコンパイラが認識して最適化を追加できるようにします。
string strLine;//not constant
int index = 0;
const int strLenght = strLine.Length();
while(index < strLine.length()){//strLine is not modified};
とにかくLength()メソッドにアクセスするときに、コンパイラ自体がこれらの最適化を行う可能性があります。
編集:私のアセンブリは少し錆びていますが、評価は一度だけ行われると思います。このコードを考えると:
int main()
{
std::string strLine="hello world";
for (int i=0; i < strLine.length(); ++i)
{
std::cout << strLine[i] <<std::endl;
}
}
このアセンブリを生成します:
for (int i=0; i < strLine.length(); ++i)
0040104A cmp dword ptr [esp+20h],esi
0040104E jbe main+86h (401086h)
しかし、このコードの場合
std::string strLine="hello world";
const int strLength = strLine.length();
for (int i=0; i < strLength ; ++i)
{
std::cout << strLine[i] <<std::endl;
}
これを生成します:
for (int i=0; i < strLength ; ++i)
0040104F cmp edi,esi
00401051 jle main+87h (401087h)
const 修飾子を使用しない場合も同じアセンブリが生成されるため、この場合は違いはありません。
VSC++ 2005 で試した
前述のように、string::length
関数はヘッダーで完全に定義されている可能性が高く、O(1) である必要があるため、単純なメンバー アクセスに評価され、コードにインライン化されることはほぼ確実です。文字列を揮発性として宣言しないため、コンパイラは外部コードが文字列を変更しないと想定し、単一のメモリアクセスへの呼び出しを最適化し、それが良いアイデア。
自分で値を取得してキャッシュすることで、コンパイラが同じことを実行できる可能性が高くなります。多くの場合、コンパイラは文字列の長さをスタックに書き込むコードを生成することさえせず、単にレジスタに残します。もちろん、コンパイラがインライン化できない別の関数を呼び出す場合は、関数呼び出しがレジスタを乱用するのを防ぐために、値をスタックに書き込む必要があります。
strLine.length() は評価されます while( i < strLine.length() )
文字列が一定の場合、ほとんどのコンパイラはこれを最適化します(適切な設定で)。
文字列を変更していないので、使用すべきではありません
const string strLine;
コンパイラは、何が変更可能で何が変更できないかについて、より多くの情報を取得するためです。ただし、C++ コンパイラがどれほどスマートになるかは正確にはわかりません。
strLine.length()
ループを一周するたびに評価されます。
nLength
特にstrLine
長い場合は、使用する方が効率的であるという点で正しいです。