7

私は、C++ アプリケーションで次のような文字列を渡すことに慣れています。

void foo(const std::string& input)
{
  std::cout << input.size() << std::endl;
}

void bar()
{
  foo("stackoverflow");
}

今、文字列を NULL にしたい場合があります:

void baz()
{
  foo("stackoverflow");
  foo(NULL); // very bad with foo implementation above
}

次のように変更できますfoo

void foo(const std::string* input)
{
  // TODO: support NULL input
  std::cout << input->size() << std::endl;
}

しかし、文字列リテラルを渡すか、 achar*をその実装にコピーするには、次のfooように記述する必要があります。

void bar()
{
  string input("hi"); // annoying temporary
  foo(&input);
  foo(NULL);  // will work as long as foo handles NULL properly
}

std::stringプロパティを継承して追加することを考え始めましnullたが、それが良い考えかどうかはわかりません。NULL になる可能性のあるパラメーターには単純にconst char*文字列を使用する方がよいかもしれませんが、メモリを自分で管理せずに文字列 (または NULL) のコピーを保存したい場合はどうすればよいでしょうか? ( C スタイルの文字列を使用することの欠点は何ですか?などを参照してください。)

賢い解決策はありますか?

4

7 に答える 7

20

型を null にする場合は、ポインターにします。これはまさにポインタができることであり、参照はできないため、参照の代わりに文字列ポインタを渡します。参照は常に同じ有効なオブジェクトを指します。ポインターは、null に設定するか、再配置して別のオブジェクトを指すことができます。したがって、ポインタができることが必要な場合は、ポインタを使用してください。

または、boost::optional を使用します。これにより、「この変数には値が含まれる場合と含まれない場合があります」を指定する、よりタイプ セーフな方法が可能になります。

または、もちろんセマンティクスを変更して、null の代わりに空の文字列を使用するか、文字列が使用可能かどうかを指定する別の bool パラメーターを渡すか、リファクタリングして最初からこれを必要としないようにします。

于 2008-12-04T19:36:14.997 に答える
11

救助のための関数のオーバーロード...

void foo( const std::string& input )
{
    std::cout << input << std::endl;

    // do more things ...
}

void foo( const char* input )
{
    if ( input != NULL ) foo( std::string(input) );
}

これは、c スタイルの char 配列と std::strings の両方を受け入れ、文字列リテラルまたは char 配列を渡すと、スタックに余分なオーバーヘッドが発生しますが、実装を 1 か所に保持し、適切な構文を維持することができます。 .

于 2008-12-04T19:44:15.573 に答える
10

個人的には、NULL の代わりに空の std::strings を渡すようにセマンティクスを変更します。

void foo(const std::string& input)
{
    if (!input.empty())
        std::cout << input.size() << std::endl;
}

void bar()
{
      foo("");
}
于 2008-12-04T19:33:00.433 に答える
3

または、以前の 2 つの回答を少し混ぜ合わせます。

void fooImpl( const char* input )
{
    if ( input != NULL )
        std::cout << input << std::endl;
}

void foo( const std::string& input )
{
    fooImpl(input.c_str());    
}

void foo( const char* input )
{
    fooImpl(input);
}

同じインターフェイス、スタックにコピーはありません。必要に応じて、fooImpl をインライン化することもできます。

于 2008-12-04T20:11:18.857 に答える
2

関数をオーバーロードして、2 番目のオーバーロードに引数を与えないのはなぜですか? 次に、両方のオーバーロードが、読み取りロジックを提供する関数を内部的に呼び出すことができ、それ自体に へのポインターが渡されstd::stringます。

void foo_impl(string const* pstr) { … }

void foo(string const& str) {
    foo_impl(&str);
}

void foo() {
    foo_impl(0);
}
于 2008-12-04T19:37:11.487 に答える
2

絶対に継承しないでくださいstd::string。継承は、C++ で使用できる最も緊密な結合であり、null 可能性のみを探しています。これはconst char*、 、オーバーロード、またはstd::string *本当に必要な場合に簡単に取得できます。

于 2008-12-05T03:05:32.110 に答える
1

単に使用するとどうなりますか:

void foo(const char *xinput)
{
    if (xinput == NULL) {
        // do something exceptional with this
        return;
    }
    std::string input(xinput);
    // remainder of code as usual
}

はい、これには追加の割り当てとコピーが発生します。関数の呼び出しは、通常の場合に使用する必要があるため、もう少し冗長ですが、必要な.c_str()セマンティクスが得られます。

于 2008-12-04T19:28:31.487 に答える