I’m not very experienced programmer in C++ and I have a problem which I can’t resolve. The project on which I’m working is quite big so I can’t post here all codes. It is too much code and too much explanation. I write just little part of code, the part which causes me problem, so I hope it is enough. Sorry for the long of my question but I want explain all posted code. Maybe this part of code isn’t enough to solve the problem but I want to try it.
First I have a struct called “record”:
struct record {
vector<string> dataRow;
vector<string *> keys;
vector<string *> values;
void setDataRow(vector<string> r) {
dataRow = r;
}
}
Some of string data are marked as keys and others as values. I next processing is better for me to have all string data in one vector, so that’s the reason why I don’t have two vectors of string (vector keys, vector values).
Then I have this:
vector< vector<record> > resultSet;
vector is like data table – set of lines with string data. I need specific count of these tables, therefore vector of vectors of records. The count of tables is optional, so when I set table count I prepare tables by reserve function:
resultSet.reserve(count);
for(unsigned int i = 0; i < count; i++) {
vector<record> vec;
resultSet.push_back(vec);
}
When I want add new record to resultSet I know the number of table to which I need insert record. After resultSet[number].push_back(rec) I need change pointers in vectors “keys” and “values” because push_back() creates new copy of “rec” with values of “dataRow” in other memory addresses, right? So I have this function which does push_back and updates pointers:
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...
}
}
}
This is working. After push_back in newRec I did control cout of inserted pointers and their referenced values, and everything was ok.
But! After some inserts I call function processData(resultSet), which has to process all data in resultSet. Before implementing processing od data I just wanted print all keys for control to find out if everything is alright. This code:
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;
}
}
is bad (Same problem with printing values vector of record). It throws exception of Access violation reading. I know that this exception is thrown when I want to read unaccessible memory, right? Please, tell me that I have mistake in code written above because I really don’t know why it doesn’t work. Before processing resultSet I do nothing with resultSet except some count of inserts.
Thank you for reading and possible answers.
If I have understood your question correctly, the reason for all this is a fundamental misconception in the way vectors behave.
Your code stores pointers in a vector that points to memory locations allocated by another vector. That would be fine if the vectors didn’t change.
The reason for this is that a std::vector is a container that makes a guarantee – all the data it contains will be allocated in a contiguous block of memory.
Now, if you insert an element into a vector, it may move memory locations around. Hence, one of the things you should know is that iterators need to be considered invalid when a vector changes. Iterators are sort of a generalized pointer. In other words, pointers to the locations of elements inside a vector become invalid too.
Now, let’s say you updated all your pointers, everywhere, when any of the vectors involved changed. You would then be fine. However, you’ve now got a bit of an uphill battle on your hands.
As you’ve said in your comments, you’re using pointers because you want efficiency. Your struct is essentially a collection of three strings. Instead of using your own struct, typedef a std::tuple (you will need a C++11 compiler) of 3 std::strings.
Finally, when you need to access the data within, do so by const reference and const_iterator unless you need to modify any of it. This will ensure that
Hope this helps.