4

これはインタビューからの質問であり、コードは以下に添付されています。この関数の何が問題になっていますか?

string f() {
    return "hello world";
}

私にとっては、まったく問題はなく、この関数を使用してプログラムを実行することもできます。

#include <iostream>
#include <string>
using namespace std;

string f() {
    return "hello world";
}

int main() {
    string s2=f();
    cout<<s2<<endl;
}

この関数の何が問題になっていますか?

4

3 に答える 3

6

ええと... このタイプの質問には、正しい答えがないことが多いという慰めがあります. あなたが望む最善の方法は、長所と短所を詳しく説明する専門知識を示して、面接官を驚かせることです (別名、コード マスターベーション)。

スタイルの観点からすると、値による戻りを使用してオブジェクトを返すのはよくありません。検討:

X f();

X x;
x = f();

f は X を割り当てます。X を返す必要があるため、追加のコピーが戻り値としてスタックに配置されます。最後に、スタック上の x が代入演算子を介して x にコピーされます。全部で 3 つの X がメモリに表示されます。これは非効率的であると言う人もいるかもしれません。そのため、値がオブジェクトの場合は、値で返さないようにする必要があります。

しかし、より知識のあるインタビュー対象者は、次のように指摘したかもしれません。

  • 一部のコンパイラは一時コピーを最適化します。この最適化では、X への代入がコピー コンストラクターまたは等値演算子のどちらを介して行われるかによって違いが生じると思います。「コピー エリソン」と言って、インタビュアーが眉をひそめるかどうかを確認します。コピー エリソンとは何ですか。. 関数が同じ翻訳単位にあり、したがって簡単にインライン化できるため、ここでコピー省略が機能する可能性があると思います。
  • 文字列クラスのほとんどすべての実装は、コピー オン ライトを実装します。この場合、文字列のテキスト データの 1 つのコピーが保持され、残りのデータはスタックに保持され、文字列が処理されたときに基本的にオーバーヘッドは発生しません。コピーしました。
  • 初期バージョンの Visual Studio を使用している場合、メモリ リークが発生します。

それで、どのような選択肢がありますか?

X const& f();

代替案として考えられます。ただし、結果の有効期間が呼び出し元には不明であるため、これには独自のスタイルの問題があります。次の呼び出しで前回の参照結果が無効になるのではないでしょうか? 知るか?

また

void f_get( X& result );

好まれるかもしれません(一部のインタビュアーによって)。これには次の利点があります。

  • ローカル変数のコピーなし
  • 不確定な寿命の参照/ポインターはありません
  • X が文字列の場合、テキスト データのコピーは 1 つだけ保持されます (ただし、これはすべての場合に当てはまります)。

どちらの場合も、可読性が犠牲になります。一般的なケースでは、呼び出し元は、どの引数が関数パラメーターで、どの引数が結果を保持することを意図しているかに、より注意を払う必要があります。

OPでは、スタック上の文字列リテラルの寿命が何であるかも明らかではないかもしれません。関数が戻ったときにスタックから割り当て解除されますか? おそらくそうではありません-おそらく静的メモリ領域に保持されます(仕様を確認するのが最善です)。しかし、そうでない場合、結果はどうなりますか-文字列はおそらくヒープ上に char const* のコピーを作成するため、コンストラクターがリテラルと非リテラルパラメーターの区別を巧妙なテンプレートタイプで伝えることができる奇妙な文字列実装を持っていない限り、おそらく何もありません・特性魔法。

いずれにせよ、このようにいくつかの BS を吐き出すことは、的外れであれば、確実にポイントを獲得していただろう.

また、コンソールは ascii、UTF-8、または unicode を出力していますか? このプログラムはおそらくそのうちの 1 つで間違っており、英語を話さないロケールでも間違っています。

stdout が有効な値であることを確認しましたか、または Windows で -D_CONSOLE を使用せずにコンパイルしていますか、または stdout が利用できず、ログ ライブラリにリダイレクトする (またはクラッシュする) 必要がある組み込みデバイスまたはゲーム コンソール用ですか?

ええと...あなたの選択をしてください。

一時的なコピーは、おそらく彼らが探していたドロイドでした. 文字列の場合、テンポラリは重要ではないため (おそらくコピー省略も)、インタビュアーはエルダーベリーのにおいがし、母親はヤギでした。

于 2013-11-01T02:42:34.520 に答える
4

この機能自体には何も問題はありません。完全に安全です。ただし、この関数は常に同じ文字列を返すため、次のように関数を定数に置き換えることを検討できます。

static const string kHelloWorld = "hello world";

cout << kHelloWorld << endl;

これには、この文字列のコピーが 1 つしかなく、名前付き定数になっているという利点があります。つまり、必要に応じて変更でき、関数呼び出しと戻りのオーバーヘッドがありません。

お役に立てれば!

于 2013-11-01T02:12:08.223 に答える
2
string f() {
    return "hello world";
}

templatetypedef の回答によると、これにより、各使用ポイントで一時的な文字列が作成されます。

これには次の方法で対処できます。

const std::string& f()
{
    static const std::string hw("Hello world");
    return hw;
}

このように、複数回の呼び出しで、最初の呼び出しで作成された単一の std::string インスタンスへの参照が生成されます。呼び出されるたびにパフォーマンス コストが発生する可能性がありますがf()、ローカルの statics が既に作成されているかどうかを確認します。これに対するコンパイラの最新のアプローチが何であるかはわかりません。

対照的に、クライアントコードが定数を直接使用している場合...

static const std::string hw("Hello world");

...その後、実行時に決定される値を返したい場合は、すべてのクライアントにコードを更新してもらう必要があります (たとえば、環境変数に基づく言語で「Hello world」メッセージを選択するなど)。関数にこだわる場合は、参照を返す定数を選択するか、戻り値の型を定数参照から値に変更して、動的に生成されたテキストを返すことができます。

于 2013-11-01T02:45:51.233 に答える