访问违反读取 - 字符串指针向量在字符串向量中的值

时间:2013-02-04 19:21:37

标签: c++

我不是很有经验的C ++程序员,我遇到了一个无法解决的问题。我工作的项目非常大,所以我不能在这里发布所有代码。这是太多的代码和太多的解释。我只写了一小部分代码,这部分导致了我的问题,所以我希望它足够了。对不起,我的问题很长,但我想解释所有发布的代码。也许这部分代码不足以解决问题,但我想尝试一下。

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

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

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

某些字符串数据标记为键,其他字符串数据标记为值。我接下来的处理对我来说更好的是将所有字符串数据放在一个向量中,这就是为什么我没有两个字符串向量(向量键,向量值)的原因。

然后我有了这个:

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)之后我需要在向量“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;
    }
}

是坏的(与记录的打印值向量相同的问题)。它抛出了Access违规读取的异常。我知道当我想要读取无法访问的内存时抛出此异常,对吧?请告诉我,上面写的代码有误,因为我真的不知道为什么它不起作用。在处理resultSet之前,除了一些插入计数之外,我对resultSet没有任何作用。

感谢您阅读和可能的答案。

3 个答案:

答案 0 :(得分:5)

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

以下是出错的代码。

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

如果keysvalues指向dataRow中的字符串,则dataRow增长时,它们将无效。

答案 1 :(得分:5)

如果我正确理解了你的问题,那么所有这一切的原因都是the way vectors behave中的一个根本误解。

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

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

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

现在,让我们说当你涉及的任何向量发生变化时,你到处都更新了所有的指针。那你就没事了。但是,你现在手上有一场艰苦的战斗。

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

最后,当您需要访问其中的数据时,请使用const引用和const_iterator执行此操作,除非您需要修改其中的任何数据。这将确保

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

答案 2 :(得分:1)

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

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

实际上,默认复制构造函数和复制operator=执行成员明确复制。这对于dataRow字段(vector<string>)是可以的,但这对于密钥和values字段来说是不好的(因为它们是原始指针的向量,他们的价值被复制了,但他们指出了一些错误的东西。)

我会重新考虑你的设计,例如对vector<int>vector<string *>字段使用keys代替values。存储的int将是dataRow向量中的索引。

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

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

或者只使用旧的C ++ 98/03样式传递const ref:

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