1

我在 C++ 方面不是很有经验的程序员,我有一个我无法解决的问题。我正在从事的项目很大,所以我不能在这里发布所有代码。这是太多的代码和太多的解释。我只写了一小部分代码,这部分给我带来了问题,所以我希望它就足够了。很抱歉我的问题很长,但我想解释所有发布的代码。也许这部分代码不足以解决问题,但我想尝试一下。

首先,我有一个名为“record”的结构:

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

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

一些字符串数据被标记为键,而另一些则被标记为值。我的下一个处理最好将所有字符串数据放在一个向量中,这就是我没有两个字符串向量(向量键、向量值)的原因。

然后我有这个:

vector< vector<record> > resultSet;

矢量就像数据表 - 带有字符串数据的行集。我需要这些表的具体计数,因此需要记录向量的向量。表格的数量是可选的,所以当我设置表格数量时,我通过保留功能准备表格:

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

当我想向 resultSet 添加新记录时,我知道我需要插入记录的表的数量。在 resultSet[number].push_back(rec) 之后,我需要在向量“keys”和“values”中更改指针,因为 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

当您向 a 添加条目时std::vector,指向该向量中元素的所有现有指针都应视为无效。

这是出错的代码。

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

如果keysvalues指向的字符串在它们增长dataRow时将变为无效。dataRow

于 2013-02-04T19:41:20.230 回答
5

如果我正确理解了您的问题,那么这一切的原因是对向量行为方式的基本误解。

您的代码将指针存储在一个向量中,该向量指向另一个向量分配的内存位置。如果向量没有改变,那会很好。

这样做的原因是 std::vector 是一个容器,它可以保证 - 它包含的所有数据都将分配在一个连续的内存块中。

现在,如果将元素插入向量中,它可能会移动内存位置。因此,您应该知道的一件事是,当向量发生变化时,迭代器需要被视为无效。迭代器是一种通用指针。换句话说,指向向量内元素位置的指针也变得无效。

现在,假设当涉及的任何向量发生变化时,您在任何地方都更新了所有指针。那你就没事了。但是,您现在面临着一场艰苦的战斗。

正如您在评论中所说,您使用指针是因为您想要效率。您的结构本质上是三个字符串的集合。不要使用您自己的结构,而是键入 3 个 std::strings 的std::tuple(您将需要 C++11 编译器)。

最后,当您需要访问其中的数据时,请通过 const reference 和 const_iterator 进行访问,除非您需要修改其中的任何内容。这将确保

  1. 您没有重复数据
  2. 您正在最大限度地利用 STL,从而最大限度地减少您自己的代码和可能的错误
  3. 您依赖于已经非常高效的算法和容器
  4. 您正在以应有的方式使用 STL。

希望这可以帮助。

于 2013-02-04T19:54:11.200 回答
1

一个可能的问题可能是实例的副本record

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

事实上,默认的复制构造函数和复制operator=是按成员复制的。这对于dataRow字段(它是 a vector<string>)是可以的,但这对于键和values字段是不好的(因为这些是原始指针的向量,它们的值被复制,但它们指向错误的地方)。

我会重新考虑您的设计,例如使用vector<int>而不是vector<string *>forkeysvalues字段。存储的ints 将是dataRow向量中的索引。

另一个注意事项(与您的问题没有直接关系)。在 C++11 中,当您想要复制某些内容时,您可能希望按值传递,并从值移动:

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

或者只是使用通过 const ref 传递的旧 C++98/03 风格:

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