17

C ++(MFC)のマルチスレッドデスクトップアプリケーションがあります。現在、開発者は、おそらく気分に応じて、CStringまたはstd::stringのいずれかを使用しています。したがって、単一の実装(おそらく、これら2つ以外のもの)を選択したいと思います。

MFCのCStringは、コピーオンライト(COW)イディオムに基づいており、マルチスレッド環境では受け入れられないと主張する人もいます(おそらくこの記事を参照してください)。アトミックカウンターは非常に高速であるように思われるため、このような主張には納得できません。また、このオーバーヘッドは、メモリの再割り当ての削減によって何らかの形で補われます。

std :: stringの実装はコンパイラに依存することを学びました。これは、MSVCではCOWではありませんが、gccであるか、そうでした。私が理解している限り、新しいC ++ 0x標準は、COW以外の実装を要求することでこれを修正し、連続したバッファー要件などの他の問題を解決します。したがって、実際にはstd::stringはこの時点では明確に定義されていないように見えます...

std :: stringについて私が気に入らないことの簡単な例:過度の再割り当てなしに関数から文字列を返す方法はありません(値で返す場合はコンストラクターをコピーし、それを最適化するための内部バッファーへのアクセスがないため、「return参照による」例えばstd::string& Result、役に立たない)。これは、CStringを使用して、値で返す(COWによるコピーなし)か、参照で渡してバッファーに直接アクセスすることで実行できます。繰り返しになりますが、C ++ 0xはその右辺値参照で救助されますが、最も近い機能にC++0xを含めることはありません。

どの文字列クラスを使用する必要がありますか?COWは本当に問題になることができますか?他に一般的に使用される文字列の効率的な実装はありますか?ありがとう。

編集:現時点ではUnicodeを使用しておらず、必要になる可能性はほとんどありません。ただし、Unicodeを簡単にサポートできるものがある場合(ICUを犠牲にすることなく...)、それはプラスになります。

4

7 に答える 7

16

私は使用しますstd::string

  • MFCからのデカップリングを促進する
  • 既存のC++ライブラリとのより良い相互作用

「値によるリターン」の問題は、ほとんど問題ではありません。コンパイラーは、戻り値最適化(RVO)の実行に非常に優れており、値で返す場合、ほとんどの場合、実際にはコピーが削除されます。そうでない場合は、通常、関数を微調整できます。

COWは、スケーリングが行われず(十分に)、期待されていた速度の向上が実際に測定されていないという理由で拒否されました(ハーブサッターの記事を参照)。不可分操作は、見た目ほど安くはありません。モノプロセッサのモノコアでは簡単でしたが、現在はマルチコアがコモディティであり、マルチプロセッサは広く利用可能です(サーバー用)。このような分散アーキテクチャには、同期が必要な複数のキャッシュがあり、アーキテクチャが分散しているほど、アトミック操作のコストが高くなります。

スモールストリング最適化CStringを実装していますか?これは、文字列が小さな文字列(通常は数文字)にメモリを割り当てないようにする簡単なトリックです。ほとんどの文字列は実際には小さいことがわかっているので非常に便利ですが、アプリケーション内の8文字未満の文字列はいくつありますか?

したがって、を使用した場合の純利益を明確に示す実際のベンチマークを提示しない限り、標準を使用することをお勧めしますCString。標準であり、おそらくより最適化されています。

于 2011-01-17T15:25:05.787 に答える
5

実際、答えは「状況によって異なります」かもしれません。ただし、MFC、IMHOを使用している場合は、CStringを使用する方が適切です。また、STLコンテナでCStringを使用することもできます。しかし、それは別の質問につながります。CStringでstlコンテナーまたはMFCコンテナーを使用する必要がありますか?CStringを使用すると、たとえばUnicode変換でアプリケーションに俊敏性が提供されます。

編集:さらに、WIN32 api呼び出しを使用すると、CString変換が簡単になります。

編集:CStringにはGetBuffer()があり、バッファを直接変更できるメソッドについて説明しています。

編集:SQLiteラッパーでCStringを使用しましたが、CStringのフォーマットは簡単です。

    bool RS::getString(int idx, CString& a_value) {

//bla bla

        if(getDB()->getEncoding() == IDatabase::UTF8){
            a_value.Format(_T("%s"), sqlite3_column_text(getCommand()->getStatement(), idx));
        }else{
            a_value.Format(_T("%s"), sqlite3_column_text16(getCommand()->getStatement(), idx));
        }
        return true;
}
于 2011-01-17T15:17:42.497 に答える
1

私は他の一般的な文字列の実装を知りません-それらはすべてC++03で同じ言語の制限に苦しんでいます。ICUコンポーネントがUnicodeに最適であるなど、特定の機能を提供するか、CStringのように非常に古いか、std::stringがそれらを凌駕します。

ただし、MSVC9 SP1 STLが使用するのと同じ手法、つまり「スワップティマイゼーション」を使用できます。これは、これまでで最も陽気な名前の最適化です。

void func(std::string& ref) {
    std::string retval;
    // ...
    std::swap(ref, retval); // No copying done here.
}

デフォルトのコンストラクターに何も割り当てない(またはSTL実装をチェックする)カスタム文字列クラスをロールした場合、それをスワップタイミング化すると、冗長な割り当てがないことが保証されます。たとえば、私のMSVC STLはSSOを使用し、デフォルトではヒープメモリを割り当てないため、上記をスワップタイミング化することで、冗長な割り当てを取得できません。

高価なヒープ割り当てを使用しないことで、パフォーマンスを大幅に向上させることもできます。一時的な割り当て用に設計されたアロケータがあり、お気に入りのSTL実装で使用されているアロケータをカスタムのアロケータに置き換えることができます。Boostからオブジェクトプールのようなものを取得したり、メモリアリーナをロールバックしたりできます。通常の新しい割り当てと比較して、10倍優れたパフォーマンスを得ることができます。

于 2011-01-17T15:19:45.377 に答える
1

「DLLごと」の決定を行うことをお勧めします。MFCに大きく依存するDLL(たとえば、GUIレイヤー)があり、CStringパラメーターを使用した多数のMFC呼び出しが必要な場合は、を使用しますCString。MFCから使用するのがCStringクラスのみであるDLLがある場合は、std::string代わりに使用してください。もちろん、両方のクラス間で変換機能が必要になりますが、すでにその問題は解決していると思います。

于 2011-01-17T17:03:39.317 に答える
1

私はいつも行くと言いstd::stringます。前述のように、RVOとNVROを使用すると、コピーによる返品が安価になり、最終的にC ++ 0xに切り替えると、何もせずに移動セマンティクスからパフォーマンスが大幅に向上しますコードを取得してATL/MFC以外のプロジェクトで使用する場合は、CStringを使用できませんstd::stringが、そこにあるので、はるかに簡単な時間を過ごすことができます。最後に、コメントで、MFCコンテナの代わりにSTLコンテナを使用していると述べました(良い動きです)。一貫性を保ち、STL文字列も使用してみませんか?

于 2011-01-17T18:12:00.477 に答える
0

特に理由がない限り、一般的な文字列テンプレートベースとしてstd::basic_stringを使用することをお勧めします。16ビット文字を処理する場合はwstringを使用するため、basic_stringと言います。

TCHARを使用する場合は、おそらくtstringをbasic_stringとして定義する必要があり、_tcslenなどの関数を使用するためにその特性クラスも実装することをお勧めします。

于 2011-01-17T15:17:37.413 に答える
-2

std::string通常は参照カウントされるため、値渡しは依然として安価な操作です(C ++ 0xの右辺値参照のものではさらにそうです)。COWは、それらを指す複数の参照を持つ文字列に対してのみトリガーされます。

std::string foo("foo");
std::string bar(foo);
foo[0] = 'm';

COWパスを通過します。COWは内部operator[]で発生するため、(非const)operator[]()またはbegin()メソッドを使用して、文字列にプライベートバッファを使用させることができます。

于 2011-01-17T15:23:28.953 に答える