Dereferencing iterator to pointers correctly - c++

I have a vector of pointers to objects. Each object stores a value and has a toString() function that returns that value as a string. I have an iterator to go through the vector and I need to extract the value of each object by calling toString(). The problem is, I can't figure out how to get the value.
This function is ultimately supposed to write the number to a file, but I'm using the cout for testing.
void writeNumbers(vector<Integer*>& input)
{
ofstream write;
write.open("Integers.txt");
vector<Integer*>::iterator iter = input.begin();
for (iter; iter < input.end(); iter++)
{
**std::cout << (*iter)->toString() << std::endl;**
}
write.close();
I get an Access Violation error which points me to the toString() function:
std::string Integer::toString()
{
std::stringstream ss;
ss << *(this)->value;
return ss.str();
}
toString() works fine whenever I don't try to access it through the iterator.
Edit: Value in toString is actually a pointer to a number.
Edit2: New writeNumbers:
void writeNumbers(vector<Integer*>& input)
{
ofstream write;
write.open("Integers.txt");
vector<Integer*>::iterator iter = input.begin();
for (iter; iter != input.end(); iter++)
{
std::cout << (*iter)->toString() << std::endl;
}
write.close();
}
Final Edit: Alright, the problem turned out to be a borked constructor that was failing to initialize a pointer properly, so I was WAY off base on where the problem actually was. :)
Integer::Integer(string input)
{
if(isNaN(input))
value = new int(atoi(input.c_str()));
}
So it should have been !isNaN, plus I fixed the problem of initializing it on bad input:
//New constructor, works 100%
Integer::Integer(string input)
{
if(!isNaN(input))
value = new int(atoi(input.c_str()));
else
value = new int(0);
}

Your toSting() has the issue. Change
ss <<*(this)->value;
to
ss << value;

EDIT: This is not an error, but a general advice when using iterators. Dont use < to check for end, use !=.
iter < input.end()
It should be like this:
iter != input.end()
This is because for certain containers, the < operator will not do what you expect. As a result, at some point you could be dereferencing input.end() itself, which points at nothing.

Related

How would you go about dereference a vector iterator

I'm currently learning C++ and ran into a little problem. The code below prints the address on the vector, but how do I let it spill the contents behind the address?
std::vector<BasePayroll*> emps;
emps.push_back(&Jane);
for (std::vector<BasePayroll*>::iterator it = emps.begin(); it != emps.end(); it++ ) {
std::cout << *it;
}
std::vector<BasePayroll*> emps; // when you dereference the iterator once you get
// what you have stored in the vector, a BasePayroll*
emps.push_back(&Jane);
for (std::vector<BasePayroll*>::iterator it = emps.begin(); it != emps.end(); it++ ) {
std::cout << *(*it); // do double dereferencing to get a BasePayroll& instead
}
You could also let a range-based for loop do the first level of dereferencing:
for(BasePayroll* pbpr : emps) {
std::cout << *pbpr;
}
For the obove to work you also need
std::ostream& operator<<(std::ostream& os, const BasePayroll& bpr) {
// output BasePayroll-data using bpr
return os;
}

Iterating over map and erasing element

I have a global variable as a map and a function that iterates over the elements of this map such as:
void printMap(){
for ( auto it = MyMap.begin(); it != MyMap.end(); ++it ){
std::cout << it->second;
}
}
which works fine.
I want to add a functionality to the function which is after printing an element, it should be erased from the map like this:
void printMap(){
for ( auto it = MyMap.begin(); it != MyMap.end(); ++it ){
std::cout << it->second;
MyMap.erase(it);
}
}
However, by adding the erase line I got an exception error of this type:
Thread 2: EXC_BAD_ACCESS (code=1, address=0x20000002)
I tried another way which is like this:
void myFunction(){
printMap();
MyMap.clear();
}
but I also got the same exception
Thread 2: EXC_BAD_ACCESS (code=1, address=0x0)
As I understand this kind of exception occurs when we refer to a memory location that does not exist. But I know it is there since the iterator got its value and it was printed. Even so I used the second method just in case that I don't refer to non-existing memory location but still I'm getting the exception.
So how can I iterate over the elements print the result then erase it?
UPDATE1
following the suggestions below and the linked topic I changed my function into this:
void printMap(){
bool i = true;
for (auto it = MyMap.cbegin(), next_it = MyMap.cbegin(); it != MyMap.cend(); it = next_it)
{
cout << it->second;
next_it = it; ++next_it;
if (i) {
MyMap.erase(it);
}
}
}
I have also tried this https://stackoverflow.com/a/42820005/7631183 and this https://stackoverflow.com/a/42819986/7631183
The problem is still not solved and I'm getting the same error
UPDATE2
I run the same exact code on a different machine and it worked fine. I still don't know what is the reason so I would guess as suggested in the comments that std::map on the first had some problems.
P.S. The first machine was a mac and the second was a Linux
When you erase an element out of the map, you have invalidated the iterator you are using in the loop, thus causing error. The erase method returns an iterator for this reason.
for( auto it = MyMap.begin(); it != MyMap.end(); )
{
std::cout << it->second;
it = MyMap.erase(it);
}
This is indeed a duplicate question. You just don't understand it well enough to see that it is a duplicate. I hope the explanation of invalidating the iterator helps clear it up.
In C++11 and later, std:map::erase() returns an iterator to the element following the one being erased. Since you are using auto, you are using C++11 or later, so you can use the new iterator when erasing while looping through the std::map, eg:
void printMap(){
auto it = MyMap.cbegin();
while (it != MyMap.cend()) {
std::cout << it->second;
it = MyMap.erase(it);
}
}
Also, since you are using C++11, your first print function can be simplified using a range-for loop:
void printMap(){
for ( auto &it: MyMap ){
std::cout << it.second;
}
}

Vector not dereferencable

After having looked at the comments I looked through the code and found an error.
It seems after some tinkering I got faced with this error:
Debug error: vector iterator is not dereferencable.
I'm 100% certain that it is in the vector inside assingthreads.
This is the newly added code that spawns the error:
void historical::writeData(std::vector<std::vector<std::wstring>> in, const string& symbol) {
std::cout << "Sending data to database connector" << std::endl;
std::vector<std::vector<std::wstring>> temp;
while (!in.empty()) {
for (int i = 0; i < 5; i++) {
temp.push_back(in.back());
in.pop_back();
}
assignthreads(temp, symbol);
temp.clear();
}
}
void historical::assignthreads(std::vector<std::vector<std::wstring>> partVec, const string& symbol) {
int i = 0;
std::thread threads[5];
std::vector<std::vector<std::wstring>>::iterator it;
for (it = partVec.end();
it != partVec.begin();
it--) {
std::shared_ptr<database_con> sh_ptr(new database_con);
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
partVec.pop_back();
i++;
}
for (auto& th : threads) th.join();
}
Your first time through the for-loop, it = partVec.end().
By definition you cannot dereference the end of a vector but you call:
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
The for loop you intended probably used reverse iterators, rbegin and rend like this:
for(auto it = rbegin(partVec); it != rend(partVec); ++it)
A couple additional notes:
Pass your vector by reference: void assignthreads(std::vector<std::vector<std::wstring>>& partVec, const string& symbol)
You need to validate that threads is the same size as partVec. So either do: vector<thread> threads(size(partVec)) or after threads is defined do: assert(size(threads) == size(partVec))
At least one issue with the for loop in assignthreads is that you attempt to dereference the end() of the vector;
for (it = partVec.end(); it != partVec.begin(); it--) {
// ...
threads[i] = std::thread(&database_con::start, sh_ptr, *it, symbol);
// ^^^^
}
And on the first iteration of the loop this is undefined; your debugger is just telling you that.
If you want to "reverse" through the loop, use the reverse_iterator of the container (available via rbegin() and rend())
for (it = partVec.rbegin(); it != partVec.rend(); ++it)
Side note it is generally not advised to modify the container whilst iterating through it (via partVec.pop_back();). Since you don't seem to do anything with what is removed from the vector, it may just as well be better to iterate over the contents, and then call std::vector<>::clear() to remove all the contents from the vector after the loop.

how to copy elements from a map to a std:: string

I am writing a program that implements a uni-directional graph using std::map. I have to search for the key and then copy the vector values corresponding to that key into a new location in the map. The problem is that the key I search for is extracted from yet another location in the map. I understand why I am getting the error as std::copy_n does not copy from vector to string (although it works the other way) but I don't know how to fix this.
I am copying a snippet of the code below:
The keys to search for are stored in a vector corresponding to key mykey.
for (
vector<string>::iterator itr = mymap.find(mykey)->second.begin()
; itr != mymap.find(mykey)->second.end()
; itr++
)
{
string find_key = "";
// error C2664 :cannot convert parameter 1 from 'std::_Vector_iterator<_Myvec>'
// to 'const std::basic_string<_Elem,_Traits,_Ax> &'
std::copy_n(itr, 1, find_key.begin());
if (mymap.find(find_key) == mymap.end())
cout << "key not found" << endl;
else
mymap[mykey].insert(
mymap.find(find_key)->second.begin()
, mymap.find(find_key)->second.end()
);
}
Edit: using stringstream to extract key from map
for (vector::iterator itr =mymap.find(mykey)->second.begin(); itr!=mymap.find(mykey)->second.end();++itr){
stringstream ss;
ss<< *itr;
string find_key = ss.str();
if (mymap.find(find_key)==mymap.end())
cout<<"key not found"<<endl;
else
for (vector<string>::iterator mapit= mymap.find(find_key)->second.begin(); mapit!=mymap.find(find_key)->second.end(); ++mapit)
mymap[mykey].push_back(*mapit);}
}
I am now getting a "vector not incrementable" error. Can you please tell me why?Also, what happens to duplicate values in a map? Thanks a lot for your help!
copy_n with a size one copy only one element (of course), but you're mixing an input iterator on string and an output iterator on char.
So do you want to read the first character of the current string or the whole string? I guess the second makes more sense in your context. So you could simple deference the iterator:
string const& find_key = *itr;
Regarding performance, even though I do not recommend to look into optimizations too earlier in the development of your software, here you perform some searches way too many times.
For example, in the first loop, at every iteration you'll look for mykey in your map. You could rewrite it using C++11 features:
for (auto const& str : mymap.find(mykey)->second)
This will call find once only and iterate on the (assuming to be existing) associated vector of string.
Here I guess that str is what you called find_key in your original post.
Then again you call three times mymap.find(find_key). That can be optimized quite easily. Additionally, does the insert compile? I couldn't find a matching function in the doc...
auto it = mymap.find(find_key);
if (it == mymap.end()) {
cout << "key not found" << endl;
} else {
auto const& vec = *it; // a ref to vector of strings to be copied
auto& dest = mymap[mykey];
dest.insert(std::end(dest), std::begin(vec), std::end(vec));
}
And when we put everything together we get:
for (auto const& find_key : mymap.find(mykey)->second) {
auto it = mymap.find(find_key);
if (it == mymap.end()) {
std::cout << "key not found" << std::endl;
} else {
auto const& vec = *it; // a ref to vector of strings to be copied
auto& dest = mymap[mykey];
dest.insert(std::end(dest), std::begin(vec), std::end(vec));
}
}
Doesn't that make more sense? ;-)

How to typcast an iterator to its original structure

list<CPoint> l;
l.push_back( CPoint(1,2) );
l.push_back( CPoint(30,40) );
l.push_back( CPoint(4,6) );
l.push_back( CPoint(70,80) );
CPoint * point = 0;
for ( list<CPoint>::iterator iter = l.begin();
iter != l.end();
iter++)
{
cout << iter->x << " , " << iter->y << endl;
// compilation error, I can't typcast it like below?
point = (CPoint *) iter;
}
The problem with above is that how to typcast the iter in the loop to the actual data structure pointer? That way I can write code like point.x, point.yto say the least.
The above is the demo code I wrote but in reality I have this code in a search function. If an item is found in the list, it will return pointer to that item otherwise NULL. In order to get that pointer back I need to dereference the iterator back to the underlying data structure pointer but how? Thanks.
To fix your syntax error, you need to dereference iterator then take the address from the underneath object:
point = &*iter;
You'd better just use std::find/std::find_if and store the iterator which is returned from std::list.
auto it = std::find_if(l.begin(), l.end(),
[](const CPoint& cp) { return cp.x == 1 && cp.y == 2; } );
if (it != l.end()) // test iterator to see is desired CPoint is found
{
std::cout << (*it).x << " " << (*it).y << std::endl;
}