Accessing Vector of Pointers? - c++

I'm trying to get a simple bit of code to work. I have a function called 'get_object_radius' which searches an area for instances of 'Creature', and pushes their pointers to a vector, then returns the vector.
I then want to cycle through them all and display their names from outside the function. I'm pretty sure I'm adding them to the vector correctly, but I'm not cycling through the vector of pointers correctly, am I?
Here's the relevant code snippet (that doesn't work):
//'get_object_radius' returns a vector of all 'Creatures' within a radius
vector<Creature*> region = get_object_radius(xpos,ypos,radius);
//I want to go through the retrieved vector and displays all the 'Creature' names
for (vector<Creature*>::iterator i = region.begin(); i != region.end(); ++i) {
cout<< region[i]->name << endl;
}
Any ideas what I'm doing wrong?

http://www.cplusplus.com/reference/stl/vector/begin/
You dereference the iterator to get to the underlying object.
cout << (*i)->name << endl;

Try:
//I want to go through the retrieved vector and displays all the 'Creature' names
for (vector<Creature*>::iterator i = region.begin(); i != region.end(); ++i) {
cout << (*i)->name << endl;
}
You need to dereference the iterator (using the * operator), which then gives you Creature* pointer.

To get the element iterator is pointing at, you dereference it (like a pointer, but iterators are not necessarily pointers). So your code should look like this:
// auto is C++11 feature
for (auto it = region.begin(); it != region.end(); ++it) {
Creature *p = *it;
std::cout << p->name << "\n";
}
In C++11 you also get range for, which hides iterators from your view:
for (Creature *p : region) {
std::cout << p->name << "\n";
}

Related

Different output using difference method of iteration over a vector C++11

I want to iterate over a vector in C++11. I used two different approach for creating a loop. The first one is using auto and the other one is getting elements by index. It is really strange for me and it would be great if someone explains to me that why the output of these two are different.
for (auto tbl = schema.begin();tbl != schema.end(); tbl++)
{
cout << "tbl: " << tbl->getTblName() << tbl->getAry()<<endl;
vector<Column> clm = tbl->getColumns();
for (int i = 0; i < clm.size(); i++)
{
cout << "clm1:" << clm.at(i).getAttrName() << endl;
}
for (auto cl = tbl->getColumns().begin(); cl != tbl->getColumns().end(); cl++)
cout << "clm2:" << cl->getAttrName() << endl;
}
The output is:
tbl: users4
**clm1:uid**
clm1:age
clm1:sex
clm1:occupation
**clm2:P�M** // this is the problem. It only prints random char.
clm2:age
clm2:sex
clm2:occupation
getColumns() is declared as return-by-value, then for auto cl = tbl->getColumns().begin();, tbl->getColumns() will return a brand-new copied vector, which is a temporary and will be destroyed after the expression immediately; so cl is always a dangled iterator, deference on it causes undefined behavior.
Another issue is that for cl != tbl->getColumns().end();, tbl->getColumns() will return another copied vector, which is a temporary too. That means you're trying to compare two iterators which belong to different containers. That's UB too.
On the ohter hand, in the 1st code sample you're using a named variable clm which represents the only one vector, which won't cause above troubles.

List Insert STL

I don't understand why my code doesn't insert to the list.
But if I change the line where I use the insert by doing a push_back the element is inserted to the list. Thank you
But I have to have it sorted. The operator < is well implemented:
void Movies::afegirPeliculaDirector(string director,string title,int year){
list<actorDirectorMovie> llista;
actorDirectorMovie dir(title,year);
int total=_mapDirectors.count(director);
if (total>0){
map<string,list<actorDirectorMovie> >::iterator
it=_mapDirectors.find(director);
llista=(*it).second;
list<actorDirectorMovie>::iterator itList=(*it).second.begin();
while(itList!=(*it).second.end() and (*itList) < dir){
itList++;
}
if (itList==(*it).second.end()) llista.push_back(dir);
else {
cout << llista.size() << endl;
llista.insert(itList,dir);//->>>>>>>>>>>>>>>>>>>> IT DOESN'T INSERT, WHY?
cout << llista.size() << endl;
}
it->second=llista;
}
else {
llista.push_back(dir);
_mapDirectors.insert(make_pair(director,llista));
}
directorMovies(director);
}
Your itList iterator refers to a different container. itList points to a member of _mapDirectors[director].second but you're trying to use it as an insertion point into llista.
I think the problem is that you wanted llista to be a reference to the mapped list, but instead you're making a copy of it.
Try the following code (I haven't tested it but it should give you the idea). Note that it takes a reference to the mapped list. There's also no need to test for an empty map, or do an explicit find for the director - you can simply reference _mapDirectors[director].second and the empty list will be created automatically if it's not already in the map.
void Movies::afegirPeliculaDirector(string director,string title,int year)
{
// get a reference to the director's list
list<actorDirectorMovie>& llista = _mapDirectors[director];
actorDirectorMovie dir(title,year);
list<actorDirectorMovie>::iterator itList=llista.begin();
while(itList!=llista.end() and (*itList) < dir){
itList++;
}
if (itList==llista.end()) llista.push_back(dir);
else {
cout << llista.size() << endl;
llista.insert(itList,dir);
cout << llista.size() << endl;
}
}
You could also consider changing from using a list to store each director's movies to a set, as this has the property of being sorted automatically for you. The whole function would be reduced to _mapDirectors[director].insert(dir) in that case.

Check if the element is the first or the last one in an std::vector

I have the following for each C++ code:
for (auto item : myVector)
{
std::cout << item;
if (item == orderBy.IsLast()) // <--- Check if this is the last element
std::cout << "(Is last element) " << std::endl;
else if (item == orderBy.IsFirst()) // <-- Check if this is the first element
std::cout << "(Is first element)" << std::endl;
}
Of course IfLast() and IfFirst() do not exist on std::vector. Is there a native std:: way to check for first and last element ?
You shouldn't use the range-based for in this case, as this kind of for "hides" the iterator, and you'd need an additional counter to keep track of the position in vector. You can simply do
for(auto it = myVector.begin(); it != myVector.end(); ++it)
{
if(it == myVector.begin()) // first element
{
// do something
}
else if(std::next(it) == myVector.end()) // last element
{
// do something else
}
}
Note that simply comparing my.Vector.back() with your element from a range-based for is OK only if you're sure that you don't have duplicates in the vector. But if e.g. the value of the last element appears multiple times in the vector, you're going to find only its first position. So that's why there's really no good way of using a range-based for without an additional index that keeps track of where exactly in the vector you are.
EDIT See also #thelink2012's answer for how to "trick" your range-based for so you can get the position of the element implicitly.
Use the std::vector::front and std::vector::back to get a reference to the data in the first and last positions.
Reference is a keyword here because you could efficiently check the address of your iterating item and the address of the respective front/back references. In your example you take the item by value not reference so this prehaps wouldn't work, take in consideration this example that'd work with this method:
for(auto& item : myVector) // take item by reference
{
std::cout << item;
if (&item == &myVector.back())
std::cout << "(last element) " << std::endl;
else if (&item == &myVector.front())
std::cout << "(first element)" << std::endl;
}
If the object overloads the address of operator & (though it's considered a bad practice) you might want to use std::addressof instead.
This method won't work however for the std::vector<bool> specialization since it optimizes the vector to store booleans efficiently with bits, and since we cannot have references to bits, all references taken out this data structure is a proxy object not exactly tied to the address of the internal data.
Use std::vector::front() for the first element.
Use std::vector::back() for the last element.
Before you call those functions, make sure that the vector is not empty.
if (!orderBy.empty() && item == orderBy.back()) <--- Check if this is the last element
else if (!orderBy.empty() && item == orderBy.front()) <-- Check if this is the first element
For value based comparison, you may use myVector.front()/myVector[0] as the first and myVector.back()/myVector[myVector.size()-1] as the last element.
Suggestion
Capture the reference by default to avoid unwanted copies. e.g.
for(const auto& I : myVector)
You could search for it again, but that would be rather inefficient. If you need information on the position of the current item, you probably want to use an iterator or an index:
for (std::size_t i=0; i<myVector.size(); ++i)
{
auto& item = myVector[i];
std::cout << item;
if (i == (myVector.size() - 1))
std::cout << "(Is last element) " << std::endl;
else if (i == 0)
std::cout << "(Is first element)" << std::endl;
}
If you have special cases for the boundaries you should use the ol' iterator version, but separating the first and last cases away from the loop.
If the cases share the code after that if you should encapsulate it on a function.
I can't write code from my phone :c
This works for me
vector<int> vi {1,2,3,4};
cout << "vi = {";
for (const auto &e : vi) {
cout << e;
if (&e != &vi.back())
cout << ',';
}
cout << '}' << endl;

multimap iterator not working

I have a Playlist class that has a vector with Tracks and each Track has a multimap<long, Note> as datamember.
class Track {
private:
multimap<long, Note> noteList;
}
Using an iterator to acces the tracks is no problem, so this part here is working fine:
vector<Track>::iterator trackIT;
try{
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}catch (int e){
cout << "exception #" << e << endl;
}
What I want to do next is iterate the Notes of each Track. But starting from this part all output is stopped. So I only get to see the first tracks name. Any cout's after that are not shown and the compiler isn't giving me any errors. Even the cout inside the try catch block isn't working..
vector<Track>::iterator trackIT;
multimap<long, Note>::iterator noteIT;
for(trackIT = this->playlist.getTracklist().begin(); trackIT < this->playlist.getTracklist().end(); trackIT++){
cout << trackIT->getTrackName() << endl;
for(noteIT = trackIT->getNoteList().begin(); noteIT != trackIT->getNoteList().end(); noteIT++){
cout << "---" << noteIT->second.getName() << endl;
}
}
cout << "random cout that is NOT shown" << endl; // this part doesn't show up in console either
Also, the method in my Track class that I'm using to add the Note objects looks like this:
void Track::addNote(Note &note) {
long key = 1000009;
this->noteList.insert(make_pair(key, note));
}
// I'm adding the notes to the track like this:
Note note1(440, 100, 8, 1, 1);
note1.setName("note1");
synthTrack.addNote(note1);
Any ideas why the iterator won't work?
Change
noteIT < trackIT->getNoteList().end()
To
noteIT != trackIT->getNoteList().end()
Not all iterators support less than / greater than comparisons.
If you have c++11 you can use a range-based for loop:
for (Note& note : trackIT->getNoteList())
Or you can use BOOST_FOREACH
BOOST_FOREACH (Note& note, trackIT->getNoteList())
You haven't shown the definitions of getTrackList or getNoteList, but there's a common mistake people make - if you return a copy of the container instead of a reference to it, the iterators will be pointing to different containers making comparisons impossible. Not only that but since the containers are temporary any use of the iterators results in undefined behavior.
If you are really hardcoding the track key, then there will only ever be one track in the map because std::map stores unique keys...
long key = 1000009; //If yo are really doing this, this key is already inserted so it will fail to insert more.
Also, if you would like a more elegant approach you could use function object.
struct print_track
{
void operator()(const Track& track)
{
cout << track.getTrackName() << endl;
std::for_each(track.getNoteList().begin(), track.getNoteList().end(), print_track_name());
}
};
struct print_note_name
{
void operator()(const std::pair<long,Note>& note_pair)
{
cout << "---" << note_pair.second.getName() << endl;
}
};
//In use...
std::for_each(playlist.getTracklist().begin(), playlist.getTracklist.end(), print_track());

Get The Object Pointer Is Pointing To

I have a QList which I have inserted pointers of objects into. I am trying to iterate through this QList to read the name of these objects. Whenever I do so, I am getting the address of these objects as oppose to reading the object name itself. I am wondering how would I be able to read the object name instead of the address?
QList<MyObject*> newObjectList;
QList<MyObject*>::iterator i;
MyObject *pNewObject = new MyObject(name);
MyObject.append(pNewObject);
for (i = newObjectList.begin(); i != newObjectList.end(); i++) {
cout << "\n" << *i << "\n";
}
When you're dereferencing i, you need to call the function to get the name from your object. Something like this:
for (i = newObjectList.begin(); i != newObjectList.end(); i++) {
// i right now is the iterator, and points to a pointer. So this will need to be
// dereferenced twice.
cout << "\n" << (*i)->getName() << "\n";
}
When you derenference the iterator i (i.e. *i) you a a reference to an object of type MyObject* which is a pointer, you have to dereference that again to get a reference to your object:
*(*i)