可能な場合はいつでもstd::string
の代わりにchar*
を使用しようとしていますが、パフォーマンスが低下しすぎるのではないかと心配しています。これは文字列を返す良い方法ですか?
std::string linux_settings_provider::get_home_folder() {
return std::string(getenv("HOME"));
}
また、関連する質問:パラメーターとして文字列を受け入れる場合、const std::string&
またはconst char*
として受け取る必要がありますか?
ありがとう。
文字列を返します。
より良い抽象化は価値があると思います。意味のあるパフォーマンスの違いを測定できるようになるまで、それはあなたの想像力にのみ存在するミクロ最適化であると主張します。
C++に文字列を抽象化するのに何年もかかりました。保守的な「使用した分だけ支払う」というforで有名なBjarne Stroustroupが、言語に明らかなパフォーマンスキラーを許可したとは思わない。より高い抽象化は良いことです。
誰もが言うように、文字列を返します。
文字列をパラメータとして受け入れる場合、const std::string&
またはconst char*
?
Constパラメータは、値で取得できるほど軽量であるか、または「上記のいずれでもない」を意味する有効な入力であるnullポインタが必要なまれな場合を除いて、参照によって取得します。このポリシーは文字列に固有のものではありません。
非定数の参照パラメーターは議論の余地があります。呼び出しコードから(優れたIDEなしで)、値または参照によって渡されるかどうかをすぐに見ることができず、違いが重要だからです。そのため、コードが不明瞭になる場合があります。 const paramsには適用されません。呼び出し元のコードを読んでいる人は、通常、それが自分の問題ではないと想定することができるため、署名を確認する必要があるのはたまにしかありません。
関数で引数のコピーを取得する場合、一般的なポリシーは値で引数を取得する必要があります。次に、使用できるコピーが既にあり、それを特定の場所(データメンバーなど)にコピーした場合は、(C++ 11で)移動したり、(C++ 03で)交換したりできます。そこに着く。これにより、呼び出し元が一時オブジェクトを渡すケースを最適化する最適な機会がコンパイラに与えられます。
特にstring
の場合、これは関数がstd::string
を値で指定し、呼び出し元は引数式として文字列リテラルまたはchar*
ヌル文字で終了する文字列を指します。 const std::string&
と関数にコピーすると、2つの文字列が作成されます。
値によって文字列をコピーするコストは、使用しているSTL実装によって異なります。
mSVCのstd :: stringは短い文字列の最適化を使用するため、短い文字列(<16文字iirc)はメモリ割り当てを必要とせず(std :: string自体に保存されます)、長い文字列はヒープ割り当てを必要とします文字列がコピーされるたびに。
gCCでのstd :: stringは、参照カウント実装を使用します。char*からstd :: stringを構築する場合、ヒープ割り当てが毎回行われますが、値を関数に渡す場合、参照カウントは単純にインクリメントされ、メモリ割り当て。
一般に、1秒間に何千回も実行していない限り、上記のことを忘れて値ごとにstd :: stringsを返す方が良いでしょう。
re:パラメーターの受け渡し。char*-> std :: stringからはコストがかかりますが、std :: string-> char *からはコストがかかりません。一般に、これは、std :: stringへのconst参照を受け入れた方がよいことを意味します。ただし、const std :: string&を引数として受け入れるための最良の理由は、呼び出し先がnullと比較するための追加のコードを必要としないことです。
特にプログラミング言語が低レベルの最適化をサポートする場合、パフォーマンスを心配するのは人間の性質です。しかし、プログラマとして忘れてはならないのは、プログラムのパフォーマンスが最適化および賞賛できる多くのことの1つにすぎないということです。プログラムの速度に加えて、私たち自身のパフォーマンスに美しさを見つけることができます。最大の視覚的出力とユーザーインターフェイスの対話性を達成しようとしながら、努力を最小限に抑えることができます。長い目で見ればビットとサイクルを心配するより多くのモチベーションになると思いますか...そう、そう、string:sを返します。コードのサイズと労力を最小限に抑え、あなたの労力を抑えます。
あなたの場合、戻り値の最適化が行われるので、std :: stringはコピーされません。
モジュールの境界を越えるときは注意してください。
C++型は、同じコンパイラの異なるバージョン間でも必ずしもバイナリ互換ではないため、プリミティブ型を返すのが最適です。
他のポスターに同意します。文字列を使用する必要があります。
ただし、コンパイラーがテンポラリーをどれだけ積極的に最適化するかによって、余分なオーバーヘッドが発生する可能性があります(動的なchar配列の使用を超える)。 (注:C++ 0aでは、右辺値参照の賢明な使用は、ここで効率を購入するためにコンパイラーの最適化を必要としないことです-プログラマーは、コードの品質に依存せずに、コードに関する追加のパフォーマンス保証を行うことができますコンパイラ。)
あなたの状況では、余分なオーバーヘッドは手動メモリ管理を導入する価値がありますか?ほとんどの合理的なプログラマーは同意しません-しかし、アプリケーションにパフォーマンスの問題が発生した場合、次のステップはアプリケーションをプロファイルすることです-したがって、複雑さを導入する場合は、改善する必要があるという十分な証拠が得られた後にのみそれを行います全体的な効率。
誰かが、戻り値の最適化(RVO)はここでは無関係であると述べました-私は同意しません。
これに関する標準テキスト(C++ 03)は(12.2)です。
[標準見積もりの開始]
クラス型の一時オブジェクトはさまざまなコンテキストで作成されます。右辺値を参照にバインド(8.5.3)、右辺値を返す(6.6.3)、右辺値を作成する変換(4.1、5.2.9、5.2.11、5.4) 、例外のスロー(15.1)、ハンドラーの入力(15.3)、およびいくつかの初期化(8.5)で。 [注:例外オブジェクトの存続期間は15.1で説明されています。 ]一時オブジェクトの作成が回避された場合でも(12.8)、一時オブジェクトが作成されたかのように、すべてのセマンティック制限を尊重する必要があります。 [例:コピーコンストラクターが呼び出されない場合でも、アクセシビリティ(11節)などのすべてのセマンティック制限が満たされるものとします。 ]
[例: struct X { X(int); X(const X&); 〜X(); }; X f(X); void g() { X a(1) ; X b = f(X(2)); a = f(a); }
ここで、実装は、Xのコピーコンストラクターを使用してX(2)に渡す前にf()を構築する一時的なものを使用する場合があります。あるいは、X(2)は、引数を保持するために使用されるスペースに構築される場合があります。また、一時的なものを使用して、Xのcopyconstructorを使用してbにコピーする前に、f(X(2))の結果を保持できます。あるいは、f()の結果はbで作成される場合があります。一方、式a = f(a)には、引数aまたはf(a)の結果の一時的な値が必要です。これにより、aの望ましくないエイリアシングを回避できます。 ]
[標準見積りの終了]
基本的に、上記のテキストは、初期化の状況ではRVOに依存することはできますが、割り当ての状況ではできないことを示しています。その理由は、オブジェクトを初期化するときに、オブジェクトを初期化するときにオブジェクト自体にエイリアスを作成する方法はないためです(コピーコンストラクターでセルフチェックを実行しないのはこのためです)。割り当てです。
本質的にRVOを禁止するコードについては何もありませんが、実際に必要な場合は、コンパイラのドキュメントを読んで、本当に信頼できることを確認してください。
私はduffymoに同意します。まず、理解できる実用的なアプリケーションを作成してから、必要に応じて攻撃の最適化を行う必要があります。この時点で、主要なボトルネックがどこにあるのかがわかり、より高速なアプリを作成するための時間をより効率的に管理できるようになります。
@duffymoに同意します。測定するまで最適化しないでください。これは、マイクロ最適化を行うときに2倍になります。そして、常に:beforeおよびafterを測定し、実際に物事をより良くしたかどうかを確認します。
文字列を返します。パフォーマンスの点でそれほど大きな損失ではありませんが、その後の作業が確実に楽になります。
さらに、関数をいつでもインライン化できますが、ほとんどのオプティマイザーはそれを修正します。
参照文字列を渡し、その文字列を操作する場合、何も返す必要はありません。 ;)