I have 3 classes, Player, Monster and Attack.
Class Player
{
public:
void addMonster(string line);
// ...
}
Class Monster
{
public:
void addAttack();
// ...
private:
vector <Attack> _attacks;
}
Now, addMonster() creates a new monster mn, and then has mn call its method addAttack() to fill the vector _attacks
void Player::addMonster(string line){
//...
Monster mn(line); //creates a new monster
while(condition){
mn.addAttack();
}
}
void Monster::addAttack(){
//...
Attack *att = gio->findAttack(ID) //takes the attack from another part of the program
_attacks.push_back(*att);
}
and it seems to work, if I check the contents of _attacks while inside addAttack(), it is pushing the correct elements in, the size of _attacks changes and evreything,
but when the program returns to addMonster(), the _attacks vector of mn is still (or again) empty, as if it had not called the method at all.
Why?
it's as if the object that gets modified is just a copy of the one calling the method, so the original one is not affected, but then how am I supposed to change that vector of that specific object?
When you create the *att pointer in your addAtack you created it on the stack. Stack variables are deleted when their original function ends and so att is deleted at the and of add attack. One possible workaround for this is to create att on the heap, using the keyword new.
Related
So I am trying to make a game in c++ with SDL2 and ran into a little problem. In order to pick up a weapon from the ground, after I check if the player is next to the object and has clicked 'E' to pick it up in an inline function from a different namespace, I have to copy its address in a variable saved in the main class and delete the object from the ground so you can't re-pick it up. It seems that after I delete the object, the pointer from the main class gets the value from another object, not that I erased.
This is the main class .h file:
class GameScene : public Scene{
public:
GameScene();
void init(SDL_Renderer* &renderer, int sceneIdx); //initialize variables
void update(int &sceneIdx, double deltaTime, Vector2f screenSize); //update frame-by-frame
void graphics(SDL_Renderer* &renderer); //render objects
void clear(); //only called on exit
private:
std::vector<SDL_Texture*> textures = {}; //all the textures present in the game
std::vector<bool> movements{false, false, false, false}; //main character movements
std::vector<Weapon> droppedWeapons = {}; //dropped pickable weapons on the ground
std::vector<Weapon*> weapons = {nullptr, nullptr}; //slots for the primary and secondary weapon
std::vector<Bullet> bullets = {}; //ssaves all the fired bullets on the map until deletion
std::unordered_map<int, SDL_Rect> positionsAtlas = {}; //offsets in textures and render scales
Cube* cube = nullptr; //main character
int mode = 0; //0=nothing, 1=weapon --unused yet
bool currentWeapon = 0; //saves what weapon is being used(primary or secondary)
int mouseX, mouseY; //mouse position on screen
};
Here is the function call in the .cpp file:
WeaponActions::pickUpWeapons(cube, droppedWeapons, weapons, pickUp/*is E pressed*/, currentWeapon);
And the function in the WeaponActions namespace:
inline void pickUpWeapons(Cube* cube, std::vector<Weapon> &droppedWeapons, std::vector<Weapon*> &weapons, bool pickUp, bool ¤tWeapon)
{
for(unsigned int i=0;i<droppedWeapons.size();i++)
{
bool type = droppedWeapons[i].getType(); //type of weapon(primary or secondary)
if(weapons[type]==nullptr && pickUp) //there is empty space in inventory
{
if(Math::checkCollision(cube->getPos(), cube->getScale(), droppedWeapons[i].getPos(), droppedWeapons[i].getScale())) //check if cube is near weapon
{
weapons[type] = &droppedWeapons.at(i); //save address
droppedWeapons.erase(droppedWeapons.begin()+i); //delete element
currentWeapon = currentWeapon == type ? currentWeapon : type; //change current weapon if necessary
i--;
}
}
}
}
The type element in the Weapon object represent if it is a primary(rifle) or a secondary(pistol) weapon.
What should I do beside creating another object to store the object the pointer is headed to?
Looks like you've got some confusion with memory and what should be a pointer. I'm looking specifically at how you store your weapons here:
std::vector<Weapon> droppedWeapons = {}; //dropped pickable weapons on the ground
std::vector<Weapon*> weapons = {nullptr, nullptr}; //slots for the primary and secondary weapon
When you pickup a weapon you are currently assigning the pointer to the memory location of the Weapon object in your vector, but then immediately removing that weapon! That's why you observed that it appears you equipped the wrong weapon. The weapon you picked up has gone, the std::vector has been resized and the next one has shunted down to fill it's place (std::vectors are required to be hold their elements in contiguous memory). This operation could also leave a dangling pointer e.g. if the weapon you tried to pick up was the last in the droppedWeapons vector your weapons pointer would end up pointing past the end of the vector.
Without changing too much of the rest of your code, you could change droppedWeapons to a vector of Weapon* (weapon pointers), rather than Weapon. Then when you want to pickup the weapon, assign the pointer to your picked up weapons list like so:
weapons[type] = droppedWeapons.at(i); // pass weapon pointer
Followed by removing the element from droppedWeapons as you are already.
Now you are working with pointers you will have to fill droppedWeapons by allocating them with new, and cleanup all your Weapon* at some point with delete.
Before making this change, it may also be a good idea to learn more about stack vs heap memory allocation in c++ if you aren't familiar with it already :)
Intro:
So my final project for this semester is to create a dungeon game that the player inputs the choices he/she has to make on the terminal and the output is his/her current status (e.g. current health, shield, damage, current room, etc). The dungeon is made up of rooms (2-dimensional, so the player could go up, down, right, left depending on the map) and the rooms have objects in them, including Monsters, NPCs, Items. Player can choose to interact with the objects (fight monsters, buy from NPCs or pick up Items) or move to another room. The professor has given us the classes' header files, and we have to simply write the cpp files for it.
Classes, members, and methods (that has to do with the problem):
Room - it has private members Room* upRoom, downRoom, leftRoom, rightRoom int index vector<Object*> objects and it basically just a linked list but 2-dimensional. Every room has its own index and vector of object pointers (might point to Item, Monster or NPC). To access the index of the room, use getIndex() function that returns the index. To access the vector objects, use getObjects() function that returns the vector.
Object - the base and abstract class, it has a private member string name, and access name using getName function. There's also a virtual function that deals with different interactions with the Player based on different children classes of Object.
Item - derived from Object, its private members are int shield, health, attack that could increase the player's states when picked up.
Game Character - derived from Object, its private members are int currentHealth, maxHealth, attack, shield (basically the status of a character)
Monster - derived from Game Character.
NPC - derived from Game Character, it has private members string script vector<Item> items items are what the player could buy from the NPC.
Player - derived from Game Character, it has a private member Room* currentRoom a pointer that points to the player's current room. We can access currentRoom using getCurrentRoom() function.
Dungeon - the dungeon itself, it has private members Player player vector<Room> rooms that make up the whole game. It has functions like runDungeon createRoom() and createPlayer. The main.cpp only consists of runDungeon.
So back to the problem:
In the createRoom() function, I declared all the things I want in the map and pushed back to vector<Room> rooms, which is a private member of Dungeon. For example:
void Dungeon::createRoom(){
//create monster
Monster zombie("Zombie", 50, 50, 10); //the ints are just the states
//create commodities
Item beef("Beef", 69, 0, 30);
Item banana("Banana", 15, 0, 5);
Item deagle("Deagle", 0, 59, 0);
//create NPC
NPC vick("Vick", "Hello there! I'm Vick the monkey.", {banana, deagle});
//create rooms
Room zero(0, {&vick}); //contains index and vector<Object*> objects
Room one(1, {&beef, &zombie})
//linking rooms together
zero.setRightroom(&one);
one.setLeftroom(&zero);
//finally pushing back the rooms declared in this function to vector<Room> rooms
rooms.push_back(zero);
rooms.push_back(one);
}
There'a a function called displayRoom() which is to print out the room index and all the objects inside it.
void Dungeon::displayRoom(){
//print out current room index, this works fine
cout << player.getCurrentRoom()->getIndex << endl;
//print out current room's first object's name, this won't work
cout << player.getCurrentRoom()->getObjects().at(0) << endl;
//print out current room's vector<Object*> size, and it always print 0
cout << player.getCurrentRoom()->getObjects().size() << endl;
}
So my problem is that the vector full of object pointers disappears. Does declaring objects inside a function and pushing them back to a private member which other functions could access work? If not, is there a solution that might help? Sorry for my bad English, I hope you understand. :)
The variables you defined in createRoom have lifetimes which end when the function returns. So pointers to them (&vick, &beef, &zombie, &one, &zero) are dangling at that point, and any use of them is undefined behavior.
To create objects which live past the end of the function where they were created, they need to be put in some other object or container, or created with new or std::make_unique or std::make_shared. You'll also want to choose one object which acts as the "owner" of each object with longer lifetime, to make sure there's simple logic in charge of cleaning up the objects' lifetime ends. Probably the Dungeon is the owner of the Rooms, a Room is the owner of its objects, and an NPC is the owner of its stock for sale. (What about the player's inventory? Can a Monster have Items? Maybe GameCharacter should have some inventory ownership logic?)
The raw pointers you've mentioned suggest you're expected to use new and delete, even though that's harder and not a great design. If you must use raw pointers, then every class which acts as an owner of other objects via raw pointers must carefully follow the Rule Of Five. In this case, copying most objects doesn't make sense, so you could delete the copy constructor and copy assignment and have move-only classes.
But if you're allowed to, it would be much easier to express ownership of pointers using std::unique_ptr. For example, change the std::vector<Object*> objects; in Room to a std::vector<std::unique_ptr<Object>> objects;. Note this should only be the "owning" pointers. The "right room", etc. properties can stay as raw pointers, since it's really the Dungeon which owns all rooms. But you do want to make sure those raw pointers are pointing at the Room objects held by the Dungeon and not copies of them.
So I did a simplified version of my code using new and I think it worked correctly:
main.cpp
#include <iostream>
#include "object.h"
#include "room.h"
using namespace std;
class dungeon{
private:
vector<room> rooms;
public:
void createMap(){
room zero, one;
object* obj;
obj = new object("sword");
zero.addObjects(obj);
obj = new object("knife");
zero.addObjects(obj);
obj = new object("zombie");
one.addObjects(obj);
obj = new object("titan");
one.addObjects(obj);
rooms = {zero, one};
}
vector<room> getRooms(){
return rooms;
}
};
int main(){
dungeon Dungeon;
Dungeon.createMap();
cout << Dungeon.getRooms().at(0).getObjects().at(0)->getName() << endl;
}
room.h
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class room{
private:
int index;
vector<object*> objects;
public:
room(){}
room(int index, vector<object*> objects){
this->index = index;
this->objects = objects;
}
int getIndex(){
return index;
}
vector<object*> getObjects(){
return objects;
}
void setIndex(int index){
this->index = index;
}
void addObjects(object* obj){
objects.push_back(obj);
}
};
object.h
#include <iostream>
#include <string>
using namespace std;
class object{
private:
string name;
public:
object(string name){
this->name = name;
}
string getName(){
return name;
}
};
Let's assume I got an abstract class ("Book" in the example below) and some derived classes ("ElectroniBook","CodingBook" in the example below). I also want to keep a vector of books in a third class ("Library") and some maps to find them. Let's also assume that I need to create the "Library" from somewhere else using the "addBook" method and then assign it in the main.
Since Book is abstract I eventually need to delete the "Book" pointers I created and I want to do it in some destructor. Neverthless, whenever I try to use delete i got this error message
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
and if I try to replace raw pointers with shared_pointers or unique_pointers I immediately get errors at compile time telling me I'm trying to use pointers that have already been deleted. Note that I'm using C++ 11.
Here's some code just for example :
class Book{
public:
Book(string name, int Npages);
virtual ~Book();
virtual void displayBook() = 0;
private:
string _name;
int _Npages;
}
class ElectronicBook : public Book{
public:
ElectronicBook(string name, int Npages);
~ElectronicBook();
void displayBook() { //do something
};
}
class CodingBook : public Book{
public:
CodingBook(string name, int Npages);
~CodingBook();
void displayBook() { // do something else
};
}
class Library{
public :
Library();
~Library(){
// this doesn't work for me
// for(auto & a : _books)
// delete a;
// _books.clear();
//
//for(int i=0;i<_bookmap.size();++i)
// delete bookmap.at(i);
};
void addCodingBook(string name, int Npages){
CodingBook* cb = new CodingBook(name, Npages);
_books.push_back(cb);
_bookmap[name] = cb;
//should I delete anything here?
};
void addEletronicBook(string name, int Npages){
ElectronicBook* eb = new ElectronicBook(name, Npages);
_books.push_back(eb);
_bookmap[name] = eb;
//should I delete anything here?
};
private :
vector<Book*> _books;
map<string, Book*> bookmap;
}
// separeted function
Library createLibrary(){
Library L;
while(...){
//read books from somewhere(file, input or whatever) and
// addElectronicBook(...)
// addCodingBook(...)
}
return L;
}
int main(){
Library myLibrary = createLibrary();
// do something with Library
}
Since I did several times "new" to add Books, I need to delete them. I tried to do it in the Library destructor like I showed but I got the error mentioned before.
If I understand correctly your issue, you are freeing twice the same memory:
// for(auto & a : _books)
// delete a;
// _books.clear();
//
//for(int i=0;i<_bookmap.size();++i)
// delete bookmap.at(i);
Both _books and bookmap contain pointers that are pointing to the same ares of memory and you are freeing them twice.
When working with raw pointers you have to decide who is the owner of the memory, say, for example _books and who has simply access to the memory but is not responsible for the cleanup.
So, you should:
delete only once, so use only one of the two for loops, say for the sake of argument _books
make sure that the other non-owning structures, say bookmap in our example, never, ever access the memory (i.e. de-reference the pointer) after the deletion
Suggestion: put in the vector unique_ptr so the vector is the owner and put raw pointers in the map to signal that the map is not owning. unique_ptr will take care of cleaning up the memory for you. If you want to be sure, add some print statements or put break points in the destructors if you have a debugger.
So I have a vector:
vector<Enemy*> enemies;
This vector hold enemies, which are created dynamically throughout the game.
if(rand() % 1000 > 998)
{
Enemy * enemy = new Enemy(num_of_enemies);
enemies.push_back(enemy);
}
The problem with this being is that the vector is ever growing even if the enemy has been deleted, which is slowing down my game.
Essentially I want to move the contents of the vector to a new one, but only the elements that actually hold an enemy.
I read that there was something called std::move but I'm not really sure how to implement it properly, or if it will successfully move the elements that contain enemies, and not just the whole vector.
Any help with code implementation of structuring would be greatly appreciated.
Here’s a complete workflow of how to handle spawning and despawning enemies. Note that there are no pointers at all involved.
Spawning an enemy:
if (if random_float_between_0_and_1() < 0.002)
enemies.push_back(Enemy{arguments});
Despawning enemies; according to your comment below, should look something like this:
auto last_iter = std::remove_if(enemies.begin(), enemies.end(), is_dead);
enemies.erase(last_iter, enemies.end());
Here, is_dead is a function that takes an Enemy const& and determines whether it collided with a player or the screen bounds:
bool is_dead(Enemy const& enemy) {
return outside_screen_area(enemy) or near_player(enemy);
}
The functions outside_screen_area and near_player should be straightforward for you to implement.
To understand how the code above works, consult the documentations of std::remove and std::vector::erase.
Another thing: implement the function random_float_between_0_and_1 in terms of the standard library random library that ships with C++11. Don’t use std::rand or modulo operations on integer random numbers, they work badly (i.e. they’re not truly uniformly distributed and will give skewed results).
The problem with this being is that the vector is ever growing even if the enemy has been deleted ...
Essentially I want to move the contents of the vector to a new one ...
It seems to me that a simpler approach would be to remove the pointers to deleted objects from the original vector instead of making a copy.
There is no difference between a pointer to a deleted object that no longer exists and a pointer to an existing object. Therefore you must keep track of the elements that must be removed from the vector. The simplest solution is to remove the element immediately after it has been deleted. This becomes much easier with smart pointers since removing the pointer also deletes the object automatically.
std::move won't help you with this problem.
You may want to consider not using manual dynamic allocation at all. You can instead store Enemy objects in the vector.
When the enemy is to be deleted I call the class destructor, and than [sic] I delete
delete expression calls the destructor. Calling it yourself also will have undefined behaviour.
First of all, I suggest you shouldn't use a data structure like std::vector if you want to remove a single element in a random position. The complexity of this operation is linear on the number of elements after the deleted element.
As I understand, you have a number of enemies moving around a 2D screen side by side with one (or many) player(s). If an enemy is hit by a player or goes out of the screen, it will be deleted. You just loop over the list of enemies to see these conditions fulfilled.
In this case, I recommend you to use std::map to manage your created enemy objects.
Suppose that your Enemy class has a function to check deletion conditions, e.g:
bool Enemy::willbeDeleted() /* if true then will be deleted */
then here is a class using std::map to manage your enemy objects:
EnemyManager.hpp
#include <map>
class EnemyManager {
public:
/*
* Get the Enemy Manager
*/
static EnemyManager& Instance();
/*!
* Delete the instance of EnemyManager
*/
static void deleteInstance();
public:
/* Create an enemy object */
void createEnemy();
/* Check all enemy objects and delete any fulfulling condition */
void checkEnemy();
virtual ~EnemyManager();
private:
/* Make sure we can not call EnemyManager constructor directly */
EnemyManager();
EnemyManager(const EnemyManager& objManager);
/* Instance of EnemyManager */
static EnemyManager* enemyManager;
private:
/* List of current enemy objects */
std::map<int, A*> enemyList_;
/* Identity of already-create object, it increases on creating a new object */
int enemyIndex_;
};
EnemyManager.cpp
#include "EnemyManager.hpp"
#include <vector>
EnemyManager* EnemyManager::enemyManager = 0;
EnemyManager& EnemyManager::Instance()
{
if (0 == enemyManager)
{
enemyManager = new EnemyManager();
}
return *enemyManager;
}
void EnemyManager::deleteInstance()
{
if (0 != enemyManager) delete enemyManager;
}
EnemyManager::EnemyManager() : enemyList_(), enemyIndex_(0)
{}
EnemyManager::~EnemyManager() {
/* Nothing todo */
}
void EnemyManager::createEnemy()
{
enemyList_[enemyIndex_] = new Enemy();
++enemyIndex_;
}
void EnemyManager::checkEnemy()
{
std::map<int, A*>::const_iterator itb = enemyList_.begin(),
ite = enemyList_.end(), it;
// Vector containing id of enemy object to delete
std::vector<int> enemyToDelete;
for (it = itb; it != ite; ++it)
if ((it->second)->willbeDeleted())
enemyToDelete.push_back(it->first);
// Delete enemies and remove them from map
for (std::size_t idx = 0; idx < enemyToDelete.size(); ++idx)
{
delete enemyList_[enemyToDelete[idx]];
enemyList_.erase(enemyToDelete[idx]);
}
}
you can use this class as follow :
in main.cpp
EnemyManager& enemyManager = EnemyManager::Instance();
if(rand() % 1000 > 998)
{
/* Create new enemy */
enemyManager.createEnemy();
}
/* Check all enemies */
enemyManager.checkEnemy();
There are two important functions: createEnemy controls the way to create a new Enemy object, checkEnemy verifies objects and deletes them if needed and the size of enemyList_ won't increase forever :)
I believe with this approach, deleting enemies won't slow down your program anymore.
One of a drawback of this approach is that the number of created objects can be limited by 2^(8*sizeof(enemyIndex_))
I am trying to create a C++ program where a user enters his choice if he wishes to add another record, and if yes then create a new object for that record.
So if I am including constructors, then how do I create a new object every time the user wants?
(If I give a predefined size to the array of object, then constructor will be called, say 50 times and initialize all 50 objects, while the user may only want to enter less).
Lets just say
class grocerylist
{
float price;
char pname;
public: grocerylist(){.....} //constructor
<some code>
.....
}
main()
{
//grocerylist a[50]; this will call constructor 50 times! which is not desired
}
My guess here is to use the new operator inside a loop, which will break when user doesnt want to enter any more records.But the problem is, it's scope will be destroyed once it comes out of a loop.
Use std::vector and just push grocerylists. Something like:
int main() {
std::vector<grocerylist> list;
[...]
while(user_wants_to_add_another_list) {
list.push(grocerylist(...));
}
}