1

私はこのように定義されたクラスを持っています:

initializer_list<string> choices;
initializer_list<string>::iterator current_choice;
bool has_choices = false;
MenuItem(Position position, string prompt) { this->position = position; this->prompt = prompt; }
MenuItem(Position position, string prompt, initializer_list<string> choices) : MenuItem(position, prompt) {
    this->choices = choices;
    this->current_choice = this->choices.begin();
    this->text = *(this->current_choice);
    this->has_choices = true;
}

現在のメニュー項目は、MenuItem * current_menu_item =&menuItems[menuItemIndex];として定義されています。

コンストラクターでinitializer_listを反復処理すると、正しい値が出力されます。しかし、コードの後半で値を切り替えようとすると、次のようになります。

 if (c == KEY_RIGHT) {
    if (current_menu_item->has_choices)
    {
        if (current_menu_item->current_choice != current_menu_item->choices.end()) {
            current_menu_item->current_choice++;
            current_menu_item->text = *(current_menu_item->current_choice);
        }
    }
}

次のメニュー項目オブジェクトのinitializer_listの最初の値が表示され、右を押すとクラッシュします。次のオブジェクトを選択して右に押すと、クラッシュします。

オブジェクトはそのようにベクトルに入れられます

menuItems.push_back(MenuItem(Position(5, 15), "Religion: ", { "*", "*", "*", "Protestant" }));
menuItems.push_back(MenuItem(Position(30, 5), "Do you smoke? ", { "Yes", "No" }));

コード全体のさまざまなポイントでデバッグを試みましたが、どこで、またはなぜそれがうまくいかないのかを正確に特定できないようです。


気にしないでください。2人の人が、initializer_listの代わりに通常のコンテナを使用する必要があると指摘しました。皆さん、答えてくれてありがとう。今は馬鹿げています。私がしなければならなかったのは、他のコードを変更せずに、initializer_listをベクトルに変更することだけでした。私はC++が大好きです。

4

3 に答える 3

3

「ワーキング ドラフト C++、2012-11-02」より

18.9 初期化リスト [support.initlist]
2 タイプ initializer_list のオブジェクトは、タイプ const E のオブジェクトの配列へのアクセスを提供します。initializer_list は、8.5.4 で指定されている初期化リストを実装するために使用されます。初期化子リストをコピーしても、基になる要素はコピーされません。— エンドノート]

たとえば、イニシャライザリストをコピーすると、std::vector完全に機能します。choicesとしてではなく、通常のコンテナとして定義する必要がありますstd::initializer_list

#include <string>
#include <vector>
#include <iostream>
#include <iterator>

struct A {
    A(const std::initializer_list<std::string> &args) : v(args) {}
    void dump() {
        std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    }

    std::vector<std::string> v;
};

int main(int argc, char **argv)
{
    A a({"Hello, ", "world!"});
    a.dump();
    return 0;
}
于 2013-03-03T00:26:17.403 に答える
1

イテレータを逆参照した後 (前ではなく) インクリメントすべきではありませんか?

current_menu_item->text = *(current_menu_item->current_choice);
current_menu_item->current_choice++;

そうしないと、終了イテレータを逆参照している可能性があります。

また、mfontanini で指摘されているように、式の最後でMenuItem(Position(5, 15), "Religion: ", { "*", "*", "*", "Protestant" })イニシャライザ リストが期限切れになるなどを呼び出すと、別の問題が発生します。これは、内部イニシャライザ リストが無効なメモリを指していることを意味します (イニシャライザ リストはポインタのみをコピーします)。解決策は、 のような標準コンテナを使用することstd::vectorです。

于 2013-03-03T00:24:50.807 に答える
0

2 番目のコンストラクターは、選択肢を値でコピーします。したがって、current_choice反復子の有効性は、パラメーターの選択の活発さに依存します。のようなパラメーター{ "*", "*", "*", "Protestant" }はスタックに保持されるため、呼び出したメソッドがpush_back返さchoicesれると消え、イテレーターとしての current_choice がぶら下がっている「ポインター」になる可能性があります。

于 2013-03-03T00:23:10.113 に答える