0

と呼ばれるカスタム文字列クラスには、プライベートメンバーを として返すだけStrの関数があります。これは、新しいものを作成した後に呼び出されたときに機能しますが、その後using を上書きすると、それを呼び出すと機能することがありますが、元のものよりも大きい場合は常に機能します。c_str()char* dataconst char* c_str() const { return data; }StrStrcinc_str()cinStr

Str b("this is b");
cout << b.c_str() << endl;

cin >> b;
cout << b.c_str() << endl;

ここで最初のものは機能しますが、行を 'b' だけb.c_str()に変更しようとすると、 'b' + 少しのゴミが出力されます。しかし、「bb」に変更しようとすると、通常は機能し、「this is b」よりも長いものに変更すると、常に機能します。Str bcin >> b;

これは奇妙です。私の istream オペレーター (フレンドになっている) は を完全に割り当て解除し、最終的Strに、読み込む各 char に対して 1 文字だけ大きい新しい char 配列を割り当ててしまうからです (機能するかどうかを確認するためだけに、機能しません)。したがって、何か他のものを読み取った後に配列を返すと、それdataが設定された新しい配列が返されるようです。

関連機能:

istream& operator>>(istream& is, Str& s) {
    delete[] s.data;
    s.data = nullptr;
    s.length = s.limit = 0;

    char c;
    while (is.get(c) && isspace(c)) ;

    if (is) {
        do s.push_back(c);
        while (is.get(c) && !isspace(c));

        if (is)
            is.unget();
    }
    return is;
}

void Str::push_back(char c) {
    if (length == limit) {
        ++limit;
        char* newData = new char[limit];

        for (size_type i = 0; i != length; ++i)
            newData[i] = data[i];

        delete[] data;
        data = newData;
    }
    data[length++] = c;
}

このように、配列はそれが保持するよりも大きな容量を持つことは決してないので、メモリガベージを出力するpush_back()方法がわかりません。c_str()

4

1 に答える 1

3

push_back()質問とc_str()コメントに基づいて、返された C 文字列c_str()が null で終了するという保証はありません。achar const*は null ターミネータなしでは文字列の長さがわからないため、これが問題の原因です!

小さなメモリ オブジェクトを割り当てると、以前に文字列クラスで使用されていて、null 以外の文字を含む小さなメモリ オブジェクトの 1 つが返される可能性があります。より大きなチャンクを割り当てると、まだヌル文字を含む「新鮮な」メモリが返されるように見え、状況がすべて問題ないように見えます。

この問題を解決するには、基本的に次の 2 つの方法があります。

  1. char const*from を返す前に null ターミネータを追加しますc_str()。今のところマルチスレッドを気にしないのであれば、これはc_str()関数で行うことができます。constマルチスレッドが重要なコンテキストでは、データ競合が発生するため、メンバー関数に変更を加えることはおそらく悪い考えです。したがって、C++ 標準文字列クラスは、変更操作の 1 つに null ターミネータを追加します。
  2. 関数をまったくサポートせずc_str()、文字列クラスの出力演算子を実装します。このように、ヌル終了は必要ありません。
于 2014-06-09T21:49:13.670 に答える