1

私は C++ の経験豊富なプログラマーではなく、解決できない問題があります。私が取り組んでいるプロジェクトはかなり大きいので、ここにすべてのコードを掲載することはできません。コードが多すぎて、説明が多すぎます。コードのごく一部、つまり問題を引き起こす部分だけを書いているので、それで十分だと思います。質問が長くて申し訳ありませんが、投稿されたすべてのコードについて説明したいと思います。コードのこの部分だけでは問題を解決できないかもしれませんが、試してみたいと思います。

まず、「record」という構造体があります。

struct record {
    vector<string> dataRow;
    vector<string *> keys;
    vector<string *> values;

    void setDataRow(vector<string> r) {
         dataRow = r;
    }
}

文字列データの一部はキーとしてマークされ、その他は値としてマークされます。次の処理では、すべての文字列データを 1 つのベクトルに収めたほうがよいため、文字列のベクトル (ベクトル キー、ベクトル値) を 2 つ持たないのはそのためです。

それから私はこれを持っています:

vector< vector<record> > resultSet;

vector はデータ テーブルのようなものです - 文字列データを含む行のセットです。これらのテーブルの特定の数が必要なので、レコードのベクトルのベクトルです。テーブルの数はオプションなので、テーブル数を設定するときは予約関数でテーブルを準備します。

resultSet.reserve(count);
for(unsigned int i = 0; i < count; i++) {
    vector<record> vec;
    resultSet.push_back(vec);
}

新しいレコードをresultSetに追加したいとき、レコードを挿入する必要があるテーブルの数を知っています。resultSet[number].push_back(rec) の後、push_back() は他のメモリアドレスに「dataRow」の値を持つ「rec」の新しいコピーを作成するため、ベクトル「キー」と「値」のポインターを変更する必要がありますよね? だから私は push_back を行い、ポインタを更新するこの関数を持っています:

void insert(int part, vector<string> & dataRow) {
    record r;
    r.setDataRow(dataRow);

    resultSet[part].push_back(r);
    int pos = resultSet.size() - 1; // position of last record
    resultSet[part].at(pos).values.clear();
    resultSet[part].at(pos).keys.clear();

    for(unsigned int i = 0; i < dataRow.size(); i++) {
        record * newRec = &resultSet[part].at(pos);
        if(isValue(dataRow[i])) {
            newRec->values.push_back(&(newRec->dataRow.at(i)));
            // control cout...
        } else {
            newRec->keys.push_back(&(newRec->dataRow.at(i)));
            // control cout...
        }
    }
}

これは機能しています。newRec で push_back した後、挿入されたポインターとそれらの参照値の cout を制御しましたが、すべて問題ありませんでした。

しかし!何回か挿入した後、関数 processData(resultSet) を呼び出します。この関数は、resultSet 内のすべてのデータを処理する必要があります。od データの処理を実装する前に、制御用のすべてのキーを出力して、すべてが問題ないかどうかを確認したかっただけです。このコード:

for(unsigned int i = 0; i < resultSet.size(); i++) {
    for(unsigned int j = 0; j < resultSet[i].size(); j++) {
        cout << "keys: ";
        for(unsigned int k = 0; k < resultSet[i].at(j).keys.size(); k++) {
            cout << *resultSet[i].at(j).keys.at(k) << ", ";
        }
        cout << endl;
    }
}

悪いです(レコードの値ベクトルを印刷する場合と同じ問題)。アクセス違反読み込みの例外をスローします。アクセスできないメモリを読み込もうとすると、この例外がスローされることはわかっていますよね? なぜうまくいかないのか本当にわからないので、上記のコードに誤りがあることを教えてください。resultSet を処理する前に、数回の挿入を除いて、resultSet で何もしません。

読んでくれてありがとう。

4

3 に答える 3

6

にエントリを追加する場合std::vector、そのベクター内の要素への既存のすべてのポインターは無効であると見なされます。

これが間違っているコードです。

vector<string> dataRow;
vector<string *> keys;
vector<string *> values;

keysで文字列をvalues指すと、大きくなるdataRowと無効になります。dataRow

于 2013-02-04T19:41:20.230 に答える
5

私があなたの質問を正しく理解していれば、これらすべての理由は、ベクトルの振る舞いにおける根本的な誤解です。

コードは、別のベクトルによって割り当てられたメモリ位置を指すベクトルにポインタを格納します。ベクトルが変更されていなければ、それで問題ありません。

この理由は、std :: vectorが保証を行うコンテナであるためです。これに含まれるすべてのデータは、連続するメモリブロックに割り当てられます。

これで、要素をベクトルに挿入すると、メモリ位置が移動する可能性があります。したがって、知っておくべきことの1つは、ベクトルが変更されたときにイテレータを無効と見なす必要があるということです。イテレータは一種の一般化されたポインタです。つまり、ベクトル内の要素の位置へのポインターも無効になります。

ここで、関係するベクトルのいずれかが変更されたときに、すべてのポインターをどこでも更新したとしましょう。そうすれば大丈夫でしょう。しかし、あなたは今、あなたの手に少し困難な戦いを持っています。

コメントで述べたように、効率が必要なため、ポインターを使用しています。構造体は基本的に3つの文字列のコレクションです。独自の構造体を使用する代わりに、3 std::stringsのstd::tuple(C ++ 11コンパイラが必要)をtypedefします。

最後に、内部のデータにアクセスする必要がある場合は、データを変更する必要がない限り、constreferenceとconst_iteratorを使用してアクセスします。これにより、

  1. データの重複はありません
  2. STLを最大限に活用しているため、独自のコードと起こりうるバグを最小限に抑えることができます。
  3. あなたはすでに本当に効率的なアルゴリズムとコンテナに依存しています
  4. 使用することを意図した方法でSTLを使用しています。

お役に立てれば。

于 2013-02-04T19:54:11.200 に答える
1

考えられる問題の 1 つは、インスタンスのコピーにある可能性があります。record

struct record 
{
    vector<string> dataRow;
    vector<string *> keys;
    vector<string *> values;
};

実際、デフォルトのコピー コンストラクターと copyoperator=は、メンバーごとの copy を行います。これはdataRowフィールド ( vector<string>) には問題ありませんが、キーとフィールドには問題があります (これらは生のポインターvaluesのベクトルであるため、それらの値はコピーされますが、何か間違ったことを指しています)。

forやfieldsvector<int>の代わりに使用するなど、あなたのデザインを再考したいと思います。格納される s は、ベクトル内のインデックスになります。vector<string *>keysvaluesintdataRow

別のメモ(問題とは直接関係ありません)。C++11 では、何かをコピーしたい場合、値で渡し、値から移動したい場合があります。

void setDataRow(vector<string> r) 
{
     dataRow = std::move(r);
}

または、古い C++98/03 スタイルの const ref による受け渡しを使用するだけです。

void setDataRow(const vector<string>& r) 
{
     dataRow = r;
}
于 2013-02-04T19:41:54.867 に答える