Searching a vector of object pointers - c++

I have a Player class where each object of the type Class has a name, wins, losses, and draws. Each object of the Player class is created by calling the_player = new Player(the_name). When the user inputs a new name to add a Player object to the program a pointer is pushed into a vector AllPlayers. The program should check before pushing the new pointer that the desired player does not already exist in said vector. I have to do this check several times throughout my program so I thought I'd write a function for it. Here is my function:
int findPlayer(vector<Player*> &vals, string name_in)
{
for (int i = 0; i < vals.size(); i++){
if (vals[i]->getName() == name_in){
cout << vals[i]->toString() << endl;
return i;
}
else
return -1;
}
};
When the option is requested to add a new Player the following code is used:
do {
cout << "Name: ";
cin >> the_name;
if (findPlayer(AllPlayers, the_name) != -1){
cerr << "Player already exists\n";
}
} while (findPlayer(AllPlayers, the_name) != -1);
the_player = new Player(the_name);
AllPlayers.push_back(the_player);
For some reason, though, every time I try to add a new player it throws "Player already exists" and never leaves the do-while loop. This is even true when the AllPlayers vector is empty. I added a cout << findPlayer(AllPlayers, the_name) for debugging and it printed 4192252 which I assume is the largest element possible in a vector.
So the question is: Why does it return 4192252 rather than -1?

If vals is empty then the for loop is never entered and the function exits without hitting a return statement. Meaning that you get a random value returned instead, in this case 4192252 happens to be in the return register. Your compiler warnings will have told you this if you read them.

What you think, what will be returned from findPlayer if vals is empty?
Is it defined?

If the vector is empty, you don't enter the loop at all, so don't reach a return statement and don't return a valid value. You should enable compiler warnings to catch this error.
Otherwise, you only check the first item, and return immediately whether or not it matched. You want to return if you find a match, but keep looking otherwise, and only return -1 if there is no match:
for (int i = 0; i < vals.size(); i++){
if (vals[i]->getName() == name_in){
cout << vals[i]->toString() << endl;
return i;
}
}
return -1;

The find player function should be something like:
int findPlayer(vector<Player*> &vals, string name_in)
{
if(vals.size() == 0)
return -1;
for (int i = 0; i < vals.size(); i++){
if (vals[i]->getName() == name_in){
cout << vals[i]->toString() << endl;
return i;
}
}
return -1;
};

Rewrite the function the following way
bool findPlayer( const std::vector<Player*> &vals, const std::string &name_in )
{
std::vector<Player*>::size_tyoe i = 0;
while ( i < vals.size() && vals[i]->getName() != name_in ) ++i;
return i != vals.size();
}
Take into account that member function getName has to be defined with qualifier const.
As for your function then it returns nothing in case when the vector is empty or returns -1 in case when the first element of the vector does not coincide with the string.
Take into account that there is standard algorithm std::find_if declared in header <algorithm> that can be used instead of your function.

Related

How can I add different names to a vector using classes?

#include <iostream>
#include <string>
#include <vector>
class Enemy
{
private:
std::string rank = "Boss";
std::string rank2 = "Miniboss";
public:
std::string type;
std::string get_rank(){
return rank;
}
std::string get_rank2(){
return rank2;
}
};
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for(size_t i; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){ // here I'm saying, if I add an enemy that's of the same type, I don't wanna add it anymore
return 1; // it returns an error, because they are the same type, so it shouldn't add it?
}
}
enemies.push_back(enemy);
}
int main()
{
Enemy enemy;
enemy.type = "Dragon";
std::cout << enemy.type << " is a " << enemy.get_rank() << std::endl;
Enemy nrone, nrtwo, nrthree, nrfour, nrfive;
// I want to add these and keep them in a vector
std::vector<Enemy> enemies;
nrone.type = "Orc";
nrtwo.type = "Goblin";
nrthree.type = "Troll";
nrfour.type = "Ogre";
nrfive.type = "Orc";
std::cout << nrfour.type << " is of rank " << nrfour.get_rank2() << std::endl;
enemies.push_back(nrone);
enemies.push_back(nrtwo);
enemies.push_back(nrthree);
enemies.push_back(nrfour);
enemies.push_back(nrfive);
std::cout << add_enemy(enemies, enemy) << std::endl;
return 0;
}
Hi, I am studying Classes & Objects in C++ right now, and I'm trying to achieve the following: create a vector of NPC monsters and add a bunch of monster types to the vector. However, if the monster/enemy is of the same type, I don't want to add it to the vector, but discard it.
In my case, I have two Orcs, so the vector should discard one of the orcs, but it doesn't, and instead if showing me a strange number on the screen.
I tried it this way and I still can't figure it out :( Any solutions?
So the reason that both Orcs are added is because by the time you run add_enemy, you've already added them. All the enemies should be using the add_enemy function instead of push_back:
int main()
{
Enemy enemy;
enemy.type = "Dragon";
std::cout << enemy.type << " is a " << enemy.get_rank() << std::endl;
Enemy nrone, nrtwo, nrthree, nrfour, nrfive;
// I want to add these and keep them in a vector
std::vector<Enemy> enemies;
nrone.type = "Orc";
nrtwo.type = "Goblin";
nrthree.type = "Troll";
nrfour.type = "Ogre";
nrfive.type = "Orc";
std::cout << nrfour.type << " is of rank " << nrfour.get_rank2() << std::endl;
enemies.push_back(nrone); //Add one Orc
enemies.push_back(nrtwo);
enemies.push_back(nrthree);
enemies.push_back(nrfour);
enemies.push_back(nrfive); //Add another Orc
std::cout << add_enemy(enemies, enemy) << std::endl; //The Orcs are already in enemies!
return 0;
}
The reason you're seeing a strange number on the screen is that if you DO successfully add an enemy, the function doesn't return anything:
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for(size_t i; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){
return 1; // Return an error
}
}
enemies.push_back(enemy); //OK, so we added the enemy, but where's the return?
}
Your add_enemies function must return a value, since it is declared as type int.
P.S... consider using a range based loop to make things a little easier:
for(Enemy& existingEnemy: enemies) {
if(enemy.type == existingEnemy.type) {
return 1;
}
}
The main problem is that you are not initializing the loop variable (i) in your add_enemy function (so the loop may never run, or it may skip some elements). Also, that function must return a value (presumably, 0) if the loop ends.
Try this:
int add_enemy(std::vector<Enemy>& enemies, Enemy enemy) // I wanna pass by reference because I want to modify the vector
{
for (size_t i = 0; i < enemies.size(); i++) { /// You forgot to initialize "i"!
if (enemies.at(i).type == enemy.type) { // here I'm saying, if I add an enemy that's of the same type, I don't wanna add it anymore
return 1; // it returns an error, because they are the same type, so it shouldn't add it?
}
}
enemies.push_back(enemy);
return 0; // The function MUST return an int value!
}
The strange number is easily explained. In your function you fail to return anything in the case where you do add the enemy. Add a return value and the strange number will go away.
int add_enemy(std::vector<Enemy>&enemies, Enemy enemy)
{
for(size_t i = 0; i < enemies.size(); i++) {
if(enemies.at(i).type == enemy.type){
return 1;
}
}
enemies.push_back(enemy);
return 0; // added a return value
}
The second problem with two orcs is also easily explained. You didn't use your add_enemy function when you added the orcs, you just used the regular vector push_back method so both orcs got added to the vector. You only used your add_enemy method for the dragon.
Also you fail to initialise i in the loop. I didn't spot that but I've corrected the code above.

C++ There is a bool return type function returning (24) here

First of all sorry for too much code
Here there is a vector (teamNum) with type class, the class contain a vector (player) with type struct, it is a little complicated, but here in this function I need to check if there is a player in teamNum which contain tName equal to _tname (function parameter) contain (the player) pID equal to _pID (function parameter)
bool thereIsSimilarID(string _tname, int _pID)
{
for (int i = 0; i < teamNum.size(); i++)
{
if (teamNum[i].tName == _tname)
{
for (int j = 0; j < teamNum[i].player.size(); j++)
{
if (teamNum[i].player[j].pID == _pID)
return true;
}
}
else if (i == (teamNum.size() - 1))
{
return false;
}
}
}
And in the main
int main()
{
cout << "\n" << thereIsSimilarID("Leverpool", 1) << endl;
}
The output is 24 !!!!!
(good note that this happen just when the team (Leverpool) is the last team in the vector teamNum)
Again sorry for too much code but I need to know the bug not only fix the problem I need to learn from you
You encountered undefined behaviour.
If you take the if (teamNum[i].tName == _tname)-branch on the last element, but find no player with the correct pID, you don't return anything. Which means, that the return value is whatever random value is currently in the memory location that should hold the return value. In your case it happens to 24. But theoretically, everything could happen.
The same problem occurs when teamNum is empty.
The solution is to make sure to always return a value from a function (except if it has return type void of course):
bool thereIsSimilarID(string _tname, int _pID)
{
for (int i = 0; i < teamNum.size(); i++)
{
// In this loop return true if you find a matching element
}
// If no matching element was found we reach this point and make sure to return a value
return false;
}
You should take a look at your compiler settings and enable all the warnings. And often it's good to let it treat certain warnings as errors.

Count word in trie implementation

I'm implementing a trie to implmenta spelling dictionary. The basic element of a trie is a trienode, which consists of a letter part (char), a flag(whether this char is the last char of a word), and an array of 26 pointers.
Private part of the TrieNode class include:
ItemType item;//char
bool isEnd;//flag
typedef TrieNode* TrieNodePtr;
TrieNodePtr myNode;
TrieNodePtr array[26];//array of pointers
This is part of the test call:
Trie t4 = Trie();
t4.insert("for");
t4.insert("fork");
t4.insert("top");
t4.insert("tops");
t4.insert("topsy");
t4.insert("toss");
t4.print();
cout << t4.wordCount() << endl;
Right now I'm trying to traverse the trie to count how many words there are (how many flags are set to true).
size_t TrieNode::wordCount() const{
for (size_t i = 0; i < 26; i++){
if (array[i] == nullptr){
return 0;
}
if (array[i]->isEnd && array[i] != nullptr){
cout << "I'm here" << endl;
return 1 + array[i]->wordCount();
}
else if(!array[i]->isEnd && array[i]!=nullptr){
cout << "I'm there" << endl;
return 0 + array[i]->wordCount();
}
else{
// do nothing
}
}
}
Every time the function returns 0. I know it's because when the first element in the array is null, then the function exits, so the count is always 0. But I don't know how to avoid this, since every time I have start from the first pointer. I also get a warning:not all control paths return a value. I'm not sure where this comes from. How do I make the function continue to the next pointer in the array if the current pointer is null? Is there a more efficient way to count words? Thank you!
Here is a simple and clear way to do it(using depth-first search):
size_t TrieNode::wordCount() const {
size_t result = isEnd ? 1 : 0;
for (size_t i = 0; i < 26; i++){
if (array[i] != null)
result += array[i]->wordCount();
return result;
}

function calling another functions gives wrong output c++?

The incrementStock function calls the "addProduct" function only if the sku string given in the argument matches with a member of the "inventory" array (the array is of type *Product and of size 50). I initialized the array to nullptr in the constructor. "num" is the increment number.
When I test it and enter a valid sku to incrementStock, I get "no space" from the addproduct function.
void Supplier::addProduct(Product *p)
{
bool space = false;
int counter=0;
while(!space && counter < inventory.size() )
{
if(inventory[counter] == nullptr )
{
inventory[counter] = p;
space = true;
}
counter++;
}
if (!space)
{
cout << "no space" << endl;
}
}
void Supplier::incrementStock(const string &sku, int num)
{
bool found = false;
for( int i = 0; i < inventory.size(); i++ )
{
if( inventory[i] && sku == inventory[i]->getSKU())
{
found=true;
addProduct(inventory[i]);
inventory[i]->setQuantity(inventory[i]->getQuantity() +num);
}
}
if (found ==false)
{
cout << "not found" << endl;
}
}
Consider this loop:
for( int i = 0; i < inventory.size(); i++ )
If you get a match for sku within this loop, it will add an extra copy of that item into inventory. That's a bit odd, but fine if you want multiple copies of the same pointer in inventory.
The problem is that after that iteration of the loop, the loop will continue, and it will also find the copy we just made, and see that it matches, and then make another copy again. This repeats until the array is full.

dynamic_bitset, crash my program

I'm new with boost. I have a program which uses dynamic_bitset inside a lambda function. After I try to run the program, I get this message. This message appears even without the function that initializes the bitset and the functions that handle it.
Does anybody know what this message means and what might be the problem?
The message:
/usr/include/boost/dynamic_bitset/dynamic_bitset.hpp:616: boost::dynamic_bitset<Block, Allocator>::~dynamic_bitset() [with Block = long unsigned int, Allocator = std::allocator<long unsigned int>]: Assertion 'm_check_invariants()' failed.
Aborted
well the code is something like this
main call to this function :
int Molecule::initSimilarity(int depth){
cout << "_size is: " << _size << "\t depth is: " << depth << endl; //TODO delete
AtomSet viewing(_size);
int m = 0;
{
// break into initial groups by symbol and valancy
for(int i=0 ; i<_size ; i++)
{
if(viewing[i]) continue;
AtomSet mask = getSetMask( //AtomSet is typedef for dynamic_bitset
[&](const Atom& b)->bool
{
return (!viewing[b._index] && b._valence == _atoms[i]->_valence && strcmp(b._symbol, _atoms[i]->_symbol) == 0);
},
[&](Atom &b)
{
b._class = m; //set the equivalence class of atom 'b' to 'm'
}
);
m++;
viewing |= mask; //viewing now contains a set of atoms and for each atom it's equivalence class
}
cout << "number of equivalence class: " << m << endl; //TODO DELETE!
}
for (int j = 0; j < depth ; j++){
AtomSet viewed(_size);
int before = m;
// iteratively refine the breakdown into groups
for (int i = 0 ; i < _size ; i++) //for any atom A
{
if (viewed[i]) continue;
viewed.flip(i);
AtomSet mask = getSetMask(//put all atoms which are equivalnt but not similar to A in
//their own equivalence class
[&](const Atom& b)->bool
{
if (viewed[b._index])
return false; //if b is in viewed return false;
if (_atoms[i]->_class == b._class) //if in the same class add b to viewed
{
viewed.flip(b._index);
bool similar = !isSimilar(*_atoms[i],b);
return similar;
}
return false;
},
[&m](Atom& b)
{
b._class = m;
}
);
if (!mask.none()) m++;
}
if (before == m){
std::cout << "Finished early after just " << j << " iterations" << std::endl;
return m;
}
}
return m;
}
the signature of getSetMask is:
AtomSet getSetMask(std::function property, std::function action);
and the weirdest thing that even when i remove all the content of that function it still give me the error message
Probably the dynamic_bitset variable that you are referencing in the lambda has gone out of scope and has already been destroyed, or something similar. (Without the source code it's difficult to be more specific)
I had that problem and it took me 3 hours to find out the problem. Here is what can happen: The operator[] in dynamic_bitset does not do bound checking. So, one value can be assigned outside of allowed range and this does not create any error (sanitizer/valgrind do not see anything) since dynamic_bitset is using 64 bit integers (on my computer at least) in order to store values. So, you can get a stored integer of 32 while you allowed only 4 bits in the dynamic_bitset. The error is triggered at a later time when m_check_invariant() is called for example when the destructor is called.
So, the problem becomes to find this range error. The solution is to edit the boost/dynamic_bitset.hpp and add print statement in the code of operator[] when an operation out of range is called. If you cannot do that then download the boost library and install it in your home directory.
I had a similar problem with dynamic_bitset that was solved by calling reset() on it before it got destroyed.
That can indicate that you are writing past the end of the bitset without resizing it. Might want to do some bounds checking.
Read the explaination of Mathieu Dutour Sikiric. The problem is that you write outside of allowed range of the bitset via operator[] and this does not create any error because it's boost and it doesn't bother to waste compute time checking that you have right to write where you want. It is C++ you know...
So to detect it, go to boost/dynamic_bitset/dynamic_bitset.hpp, and modify the code to impose checks every time you use operator[].
boost/dynamic_bitset/dynamic_bitset.hpp, around line 300.
reference operator[](size_type pos) {
assert(m_check_invariants());
return reference(m_bits[block_index(pos)], bit_index(pos));
}
bool operator[](size_type pos) const {
assert(m_check_invariants());
return test(pos);
}
This makes it easier to detect the error in your code.