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.
When you add an entry to a std::vector
, all existing pointers to elements in that vector should be considered invalid.
Here is the code that is going wrong.
vector<string> dataRow;
vector<string *> keys;
vector<string *> values;
If keys
and values
point to the strings in dataRow
they will become invalid when dataRow
grows.
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.
One possible problem could be in copies of record
instances.
struct record
{
vector<string> dataRow;
vector<string *> keys;
vector<string *> values;
};
In fact, default copy constructor and copy operator=
do a member-wise copy . This is OK for dataRow
field (which is a vector<string>
), but this is bad for keys and values
fields (since these are vectors of raw pointers , their values are copied, but they point to something wrong).
I'd reconsider your design, eg using vector<int>
instead of vector<string *>
for keys
and values
fields. The int
s stored would be indexes in the dataRow
vector.
Another note (not directly related to your problem). In C++11, when you want to copy something, you may want to pass by value, and move from the value:
void setDataRow(vector<string> r)
{
dataRow = std::move(r);
}
Or just use old C++98/03 style of passing by const ref:
void setDataRow(const vector<string>& r)
{
dataRow = r;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.