Pointer not derefencable from a list - c++

I have the two classes Player and Team, where each player class has a pointer to the team
class Player{
private: Team* team;}
and each team has a list of pointers to the players
class Team{
private: list<Player*> playerlist;}
Now i want to find out in which team a player plays by adressing him in the list. Afterwards i want to be able to adress the team for something like team.getid()
I tried the following solution for a function:
std::list<Team>::iterator* teamsearch(std::list<Team> a, int b)
{
int i = 0;
std::list<Team>::iterator Listitem;
std::list<Team>::iterator* Listitemtest = &Listitem;
while (i == 0){
for (Listitem = a.begin(); Listitem != a.end(); ++Listitem)
{
if (Listitem->getteamID_() == b)
i++;
Listitemtest = &Listitem;
}
};
std::cout << "TeamID: " << (*Listitemtest)->getteamID_() << std::endl;
return Listitemtest;
}
The idea is that it returns an iterator which points to the adress of the team of the player. Is this correct ?
Then i tried to adress the teams by dereferencing the iterator again by this way:
Team team;
team.setteamID_(0);
team.setteamname_("team1");
Teamvector.push_back(team);
std::list<Team>::iterator Listitem;
Listitem = *teamsearch(Teamvector, 0);
which gives me a runtime error in MSVC120.dll ...include\list: list iterator not derefencable
Somehow it seems like the iterator points to the end of the teamlist ?

You are returning a pointer to a local variable. The iterator which is pointed to by the returned pointer, is already destroyed when the function returns.
You should just return by value, don't bother with pointers here. An iterator is trivial to copy.

Nobody mentioned this yet but the first major problem is that this line:
Listitemtest = &Listitem;
means that Listitemtest points to the iterator Listitem itself. It does not point to the thing that ListItem is pointing at.
Therefore when you go (*Listitemtest)->getteamID_(), this is equivalent to Listitem->getteamID_(). But since Listitem is now at a.end() this is an invalid dereference.
To fix this, you should stop using pointers to iterators. I can't think of a single situation ever where it is a good idea to use pointers to iterators - instead it's a sign that you're doing something wrong.
Instead, make Listitemtest be an iterator of the same type as Listitem, and then just set it by going Listitemtest = Listitem. Then it will point that the item you are interested in.
However, having fixed this, you still can't return Listitemtest yet. That is because it points into std::list<Team> a, which is a local variable to your function and will stop existing when you return. To fix this, make the parameter be std::list<Team> &a. Then you can safely return the iterator.

Related

How do I change where the pointer is pointing to through a return statement?

I have a Player object which holds an ID string. I'm trying to check if a player already exists by iterating through a list of Players. If the player already exists, I want to return the existing player, else I will make a new instance of the Player object.
Player *playerExists(const std::string &playerId, const std::vector<Player>* players)
{
for (Player existingPlayer : *players)
{
if (existingPlayer.playerId == playerId)
{
std::cout << "Player " << existingPlayer.name << " exists" << std::endl;
return &existingPlayer;
}
}
return new Player();
}
The problem seems to be in the return statement. I don't know how to return the specific object to a pointer. This seems to be the only way I found not to get an error (talking about the & sing in the return statement).
Player* player = playerExists("SomeID", listOfPlayers);
listOfPlayers->push_back(*player);
delete player;
I'm quite new to using raw pointers so I probably just don't understand what the problem here is. I'd really appreciate if someone could explain what I'm doing wrong.
There are several issues with this code.
Each iteration of the loop is making a local copy of a Player in the vector, and thus will return a pointer to that copy if a match is found. But when the current iteration is finished, that copy is destroyed, so you will end up returning a dangling pointer. To fix that, your loop need to take a reference to each Player in the vector:
for (Player &existingPlayer : *players)
The next issue is if no match is found, you return a newed Player. The problem with that is the caller is then unconditionally deleteing the returned Player* regardless of whether it was newed or not. The vector owns the Player objects it is holding, so deleteing one of those objects is undefined behavior.
A better design choice is to have playerExists() return nullptr if no match is found. The caller can then do different things depending on whether a Player or a nullptr is returned, eg:
Player* playerExists(const std::string &playerId, const std::vector<Player>* players)
{
for (Player &existingPlayer : *players)
{
if (existingPlayer.playerId == playerId)
{
std::cout << "Player " << existingPlayer.name << " exists" << std::endl;
return &existingPlayer;
}
}
return nullptr;
}
Player* player = playerExists("SomeID", listOfPlayers);
if (player) {
// ID found, use player as needed...
listOfPlayers->push_back(*player); // why re-add a player that is already in the list???
} else {
// ID not found, do something else...
listOfPlayers->push_back(Player{}); // why add a blank player???
}
Different ways of tackling this. Biggest problem might be in this line:
for (Player existingPlayer : *players)
It's making a copy (I think) of a player already in the collection.
When you do a return &existingPlayer; ... well it's returning an object on the stack. Bad things will happen.
You are better off iterating the collection and returning an iterator, IMHO... Maybe...
std::vector<Player>::const_iterator playerExists(const std::string &playerId, const std::vector<Player>& players)
{
std::vector<Player>::const_iterator it = players.begin();
for (; it != players.end(); ++it)
{
if (it->playerId == playerId)
{
std::cout << "Player " << it->name << " exists" << std::endl;
break;
}
}
return it;
}
You can always test after the return for
With the returned iterator, you'll have to do something with it...
auto it = playerExists(playerId, players);
if (it == players.end())
{
// not found
} else {
// found
}
Your problem is there :
for (Player existingPlayer : *players)
In each iteration of the for loop, existingPlayer is a copy of the player that is stored in a local variable in the scope of the for loop. You are returning a pointer to a local variable which get destroyed after each loop iteration.
To avoid the copy, use a reference :
for (Player &existingPlayer : *players)
Here, existingPlayer is a reference to the real player. This is what you did with the const std::string in the function prototype.
I have also some suggestions to possibly improve your code :
Avoid raw C pointers, C++ give you tools to get the same functionnality in a (much) less error prone way and hence, more secure. If you don't need NULL as I see in your code, a reference is probably better. Else you should consider smart pointers (std::unique_ptr, std::shared_ptr. I would pass players by reference instead of pointers in your case.
std::vector may not be the best container. Choosing the right container can be difficult. Currently you are iterating over the whole array to find the player by his id. You should use a std::unordered_map<std::string,Player> that use a hash table and probably speed up lookup time. I would also use a typedef of std::a_container_class<Player> to avoid massive code rewrite if you change the container kind or write one from scratch later.
Does copying a Player make sense ? If not, explicitly delete copy constructor and copy assignment operator, the compiler will then throw an error if you want to copy the Player. Note that this is a design choice that I often do, multiple instance of the same player is normal depending on your objects design.
I'm doubtful about return new Player();. Why do you return a new player if no player exist ? With my personal interpretation (might be totally wrong), I would return an iterator. No pointer involved, you can "dereference" the iterator to get a reference to the found player and the non existence of player is signaled by return an iterator equal to players.end(). But beware a big drawback, be sure to test before modifying the container and invalidating iterators.
With my own design choices, I made a slightly untested and shorter version of your function :
#include <unordered_map>
typedef std::unordered_map<std::string,Player> PlayerCollection;
/* ... */
PlayerCollection::const_iterator playerExists(const std::string &playerId, const PlayerCollection &players)
{
return players.find(playerId);
}
Keep in mind that my suggestions are opinionated and are not the absolute truth. Read the other answers which have different ideas.

What is the "healthy" way to handle deletion of pointers to objects from a list?

Let's say we have a Buyer class.
Buyer has an email data member and a getEmail() member function.
Also we have a method like the one below that deletes a pointer to Buyer objects from a list of pointers to Buyer objects
void removeBuyer(Buyer* b){
list<Buyer*> :: iterator z;
for(z = buyersList.begin(); z != buyersList.end(); ){//buyersList is the list of pointers to Buyer objects
if( (*z)->getEmail() == b->getEmail() ){
z = buyersList.erase(z);
}
else
++z;
}
Then let's say I try to "log in" with the Buyer object whose pointer I just deleted.
void logIn{
cout<<"Give email"<<endl;
std::string e;
std::cin>>e;
list <Buyer*> :: iterator it;
for(it = buyersList().begin(); it != buyersList().end(); ++it){
if (e == (*it)->getEmail() ){// This is where the crash eventually occurs
//something
}
}
}
This sometimes works fine, others a crash occurs with the 0xC0000005 return.
I know that the buyersList (inside logIn) is updated and holds all the pointers minus the deleted one. And when I dereference the iterator it, it then "becomes" one of the elements of the list, hence a pointer that exists and is not deleted.
I know that I'm probably doing something wrong with the handling of the pointer I just deleted.
What am I missing exactly?
The issue was that buyersList() returned by value and not by reference.

Implementing a two-way ordered list, how do I return a pointer so that it can be given a value?

I have to implement a two way ordered list at a specific way, and I got stuck. I have a Bag class that has an Element class inside it. Now the problem is when I want to place an item inside the bag.
An element has a key (its value), a pointer to the smaller element (_down), and one to the larger (_up). When I put a new element inside the bag the code is the following (names translated so it's easier to understand)
void Bag::put(int e) {
if(_item_count==0){
Element *element = new Element(e, nullptr, nullptr);
_bot = element;
_top = element;
}else{
Element *p = _bot;
while(e > p->key()){
p = p->up();
}
Element *element = new Element(e, p, p->down());
p->down() = element;
}
}
So the problem is that p->down() is not assignable. I guess I should return the pointer by reference, but I don't know how to do that.
down() function
Bag::Element *Bag::Element::down() {
return _down;
}
I tried putting & at great many places, but I just can't figure out how to make it all work.
Here's the full code if needed, but it's variables are mostly Hungarian, I'll provide translation if needed.
Header on the left, cpp on the right
This will return a non-const lvalue reference to pointer which is good to be assigned to:
Bag::Element *&Bag::Element::down() {
return _down;
}
And perhaps you should also provide a const overload:
Bag::Element * const &Bag::Element::down() const {
return _down;
}
Of course you should update your class declaration accordingly.
And also you may consider using a struct for such simple classes like Element while making pointer fields public (which is the default access control for structs. Typically you won't get bashed for doing this in C/C++, unlike in Java where people tend to insist on using private fields and getter/setters even for super simple classes. (I'm not saying this good or bad, just a sort of convention.)
Just assign the pointer directly:
p->_down = element;
Note that you are only modifying the "down" pointer of the next element. You also need to modify the "up" pointer of the previous element.
Note also that your new element may be the first or the last element in the list, you should handle these cases specially. Once you do that, you may discover that special handling of the empty list is unnecessary.

iterating on a list of vectors of some class (C++)

Can anyone help me with why this code give me a segmentation fault on the inner loop?
I am trying to iterate on a list of vectors of some class block.
class chain
{
list<vector<Block*>* >* _blockChain;
}
Chain* someChain = new Chain();
for(list<vector<Block*>*>::iterator listIter = someChain->getChain()->end() ;
listIter != someChain->getChain()->begin(); listIter--)
{
for(vector<Block*>::iterator it = (*listIter)->begin();
it != (*listIter)->end() ; it++)
{
//do something
}
}
If you want to iterate through the list in reverse, use a reverse_iterator:
for(list<vector<Block*>*>::reverse_iterator listIter = someChain->getChain()->rbegin();
listIter != someChain->getChain()->rend(); ++listIter)
With a recent compiler you can condense that down a bit:
for (auto listIter = someChain->getChain()->rbegin();
listIter != someChain->getChain()->rend(); ++listIter)
That said, I agree with #Mike Seymour: you are using way too many pointers. For one really obvious example, there's almost never a reason to use a pointer to a vector (because a vector is itself little more than a wrapper around a pointer to data, with a little extra data for book-keeping about how much data is in the vector).
The first time you try to access listIter it is pointing to end() which is not to be dereferenced. In the inner loop you dereference listIter and try to use it.
From http://en.cppreference.com/w/cpp/container/vector/end
This element acts as a placeholder; attempting to access it results in undefined behavior.
I think you face a crash in List::end() and List::begin(). It's because you has not initialized your List anywhere in your code (at least I don't see in your given code). you need:
class Chain{
Chain()
{
_blockChain = new list<vector<int*>* >;
// initializing Vector and so on
}
};
Although this is not all of your problems.

How to implement an API for a distributed map in c++?

I am implementing a distributed map in c++ and searching for a good API design.
First and straightforward option is to make it exactly like std::map. Problem is with iterator.
IMap<std::string,Person>::iterator it;
it = map.find("sample");
if(it == map.end() ){
//NULL
}
for(it = map.begin(); it != map.end(); it++){
//iterate
}
In distributed context(at least in the one i am implementing) , there is no begin and end of the map. It is not ordered in anyway, so returning an iterator does not look like an option.
Second option is returning the value class by copy like below:
Person emptyPerson;
Person person = map.get("sample");
if(person == emptyPerson){
//NULL
}
Problem is with that NULL check looks strange. You can first ask if it is available and then get the object, but the requirement is that these operations must be atomic.
Third option is returning pointer:
Person* person = map.get("sample");
if(person == NULL){
//NULL
}
I don't want to do it this way, because it is error prone. User needs to delete the pointer that i created internally.
I am thinking about returning a class that wrapping user object like:
value_reference<std::map, Person> person = map.get("sample");
if(value_reference.hasValue() ){
Person p = value_reference;
}
So what do you think the best approach is?
Do you know any good api similar to requirements my distributed map?
Based on your term "distributed map" I am making the following assumptions:
A subset of the data is available locally, and for the set of data that is not some remote-fetch will need to be performed.
Writes to the returned object should not be automatically persisted in the data store. An explicit update request should be made instead.
If this is true then iterators are not what you want, nor do you want the STL container model. The C++ Iterator concept requires you to implement the pre-increment (++i) operator, and if your data is unordered and spread across multiple nodes, then the request "give me the next entry" does not make sense.
You could create a terrible kludge if you wanted to simulate STL containers and iterators for interoperability reasons: have the map's end() method return a sentinel iterator instance, and have operator++() for your iterators return this same sentinel. Effectively, every iterator would point to "the last element in the map." I would strongly advise against taking this approach unless it becomes necessary, and I don't think it will be.
It sounds like what you want is a simple CRUD model, where updates must be explicitly requested. In that case, your API would look something like:
template <typename TKey, typename TValue>
class IMap<TKey, TValue>
{
public:
void create(TKey const & key, TValue const & value) = 0;
std::unique_ptr<TValue> retrieve(TKey const & key) = 0;
bool update(TKey const & key, TValue const & value) = 0;
bool remove(TKey const & key) = 0;
};
In the retrieve case, you would simply return a null pointer as you suggested. std::unique_ptr<> will ensure that the caller will either delete the allocated object or explicitly take ownership of it.
An alternative to the "return pointer to newly-allocated object" case would be to let the caller pass in a reference, and the method would return true if the value was found in the map. This will, for example, let the caller retrieve an object directly into an array slot or other local structure without the need for an intermediary heap allocation.
bool retrieve(TKey const & key, TValue & value) = 0;
Use of this method would look something like:
Person person;
if (map.retrieve("sample", person)) {
std::cout << "Found person: " << person << std::endl;
} else {
std::cout << "Did not find person." << std::endl;
}
You could provide both overloads too, and the one returning a pointer can be implemented in terms of the other by default:
template <typename TKey, typename TValue>
std::unique_ptr<TValue> IMap<TKey, TValue>::retrieve(TKey const & key)
{
TValue v;
return std::unique_ptr<TValue>(retrieve(key, v) ? new TValue(v) : nullptr);
}
I'd say something like option 3 is best. You could just emulate it using one of the standard smart pointer types introduced in C++11, so you still create a pointer, but the user doesn't have to free it. So something like:
std::unqiue_ptr<Person> person = map.get("sample");
if(person) {
person->makeMeASandwitch();
}