Function that creates pointers to classes - c++

I'm trying to create a function that creates pointers to instances of a player class.
This is because, at the start of my game, I want to be able to make as many player instances as I want.
This is to make the game 2-player, 4-player or 3-player and theoretically, an infinite number of players determined by the user's input.
The problem I'm facing is that if I make my function to make pointers like this:
edit: player is a user defined class that I made.
void createPointer()
{
player * player1 = new player("George");
}
The pointer will only be locally declared, resulting in a memory leak, because I can't reference the pointer player1, as it's deleted once the function createPointer() concludes.
I wouldn't be able to reference the players correctly, as shown above, as they're all named player1. Makes me think I should use templates in c++ to change the name of the variable each time a player is created.
As shown below, I couldn't delete the player instance at the end of the game:
void endGame()
{
//delete the object the pointer
//is pointing to in memory
delete player1;
//set the pointer to point to NULL
//as default, so can check if pointer is pointing
//to anything
player1 = NULL;
}
So I was just wondering if there was another approach I could take? I know of one solution which is to globally declare the pointers beforehand. i.e
player * player1 = NULL;
player * player2 = NULL;
player * player3 = NULL;
void createPointer()
{
player1 = new player("George");
}
However, this would mean I cannot create a variable number of players without declaring all of them beforehand, as shown in the above example.
Apologies if I am misunderstanding anything, I would highly appreciate any advice whatsoever.

If you really want to use raw pointers for your players, you can modify your createPointer function to return what it has created:
player* createPointer()
{
player* createdPlayer = new player("George");
// Do whatever you need for initialization!
return createdPlayer;
}
Then, in the code that wants to use such players, do something like:
//...
player* player1 = createPointer();
player* player2 = createPointer();
//...
Then, when you've done with the players, you can just delete each one...
delete player1;
delete player2;
A better solution (IMHO) would be to put whatever code you (eventually) have in createPointer into the constructor definition for the player class; then you can just use code like player *p1 = new player("Harold"); rather than calling a function each time you make a new player.
But, as mentioned in the comments, you would be better off using either std::vector or std::shared_ptr objects.

You probably need a container of player instances. The default container is std::vector.
Something like
std::vector<player> players;
players.emplace_back("George"); // Create the first player
players.emplace_back("Fred"); // Create the next player
// etc.
You can refer to players by their (0 based) position in players
players[0].do_stuff(); // George does stuff
You can loop over all the players
for (auto & player : players) {
player.take_turn(); // each player in turn does something
}
When players is destroyed, it automatically cleans up the player objects

If i understand you correctly,maybe there are two solutions to solve your problem.
Here the codes.
#include <string>
#include <iostream>
using namespace std;
class Player
{
public:
Player(string name) :m_name(name) {
cout << "player " << m_name << " is created\n";
}
~Player()
{
cout << "pluaer " << m_name << " is destoryed\n";
}
private:
string m_name;
};
//The first solution : return a pointer
Player* creatPlayer_1(const string& name)
{
return new Player(name);
}
//The second solution : pass a reference of the pointer as the argument
void createPlayer_2(Player*& pPlayer, const string& name)
{
pPlayer = new Player(name);
}
int main()
{
Player* pPlayer_one = creatPlayer_1("one");
Player* pPlayer_two = nullptr;
createPlayer_2(pPlayer_two, "two");
delete pPlayer_one;
delete pPlayer_two;
}

Related

Copying the address of a vector element which has to be deleted afterwards

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 &currentWeapon)
{
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 :)

Cannot Access Vector in C++ Dungeon Text Game

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;
}
};

How to fix 'this was nullptr" when there was no call to "this"

Im writing a simple tictactoe game with a simple ai. Im getting an error when initializing my game object. My game object initializes two player objects that each have their player number properties set to "one" or "two" with a method called setPlayerNum. Although I never use the "this" key word I am getting an error saying that "this was a nullptr" as well as a write access violation.
I've tried derefrencing the player object as well as making the player number property a public variable (in case it was a scope error). Neither worked for me.
int main() {
game g;
g.printTitleCard();
std::system("pause");
return 0;
}
#include "board.h"
#include "player.h"
class game {
public:
game();
~game();
void printTitleCard();
void play();
int victory();
void victoryScreen(int winner);
private:
board * gameBoard;
player * player_one;
player * player_two;
int turns;
int maxTurns = 9;
};
game::game() {
gameBoard = new board();
player_one->setPlayerNum(1);
player_two->setPlayerNum(2);
}
class player {
public:
player();
~player();
void setPlayerNum(int);
int getPlayerNum();
void updateScore(int);
int retrieveWins();
int retrieveLoses();
int retrieveTies();
private:
int playerNum = 0;
int wins = 0;
int loses = 0;
int ties = 0;
};
void player::setPlayerNum(int num) {
playerNum = num;
}
game::game() {
gameBoard = new board();
player_one->setPlayerNum(1);
player_two->setPlayerNum(2);
}
does not instantiate any players before calling setPlayerNum, this means that there are no valid instances of player to invoke setPlayerNum on. After this the program has wandered into undefined behaviour and all bets are off.
Solution:
The best way to solve this is to go back a few steps to
class game {
public:
game();
~game();
void printTitleCard();
void play();
int victory();
void victoryScreen(int winner);
private:
board * gameBoard;
player * player_one;
player * player_two;
int turns;
int maxTurns = 9;
};
And NOT use pointers for gameBoard, player_one and player_two.
board * gameBoard;
player * player_one;
player * player_two;
becomes
board gameBoard;
player player_one;
player player_two;
and
game::game() {
gameBoard = new board();
player_one->setPlayerNum(1);
player_two->setPlayerNum(2);
}
becomes
game::game() {
player_one.setPlayerNum(1);
player_two.setPlayerNum(2);
}
Only use new when you absolutely have to, and in a world of library containers and smart pointers that's almost never. More on that here: Why should C++ programmers minimize use of 'new'?
Unrelated:
If you change the player constructor to take the player number as a parameter, setPlayerNum likely becomes unnecessary and game's constructor can become
game::game(): player_one(1), player_two(2){
}
Addendum
Is there any way to keep them as pointers?
Yes, but it's not a very good idea because you pick up a tonne of extra responsibilities. You nave to new the players in the constructor before you use them. You have to delete the the players and game in the destructor, and you have to write your own copy (and move if you want them) constructor and assignment (and move assignment) operators to make sure the object is copied and assigned correctly (See What is The Rule of Three? for more on why you need this stuff).
game::game() {
gameBoard = new board();
player_one = new player; //added to get player instance
player_two= new player; // added to get player instance
player_one->setPlayerNum(1);
player_two->setPlayerNum(2);
}
// new function: copy constructor
game::game(const game & source) {
gameBoard = new board(*source.gameBoard);
player_one = new player(*source.player_one);
player_two= new player(*source.player_two);
turns = source.turns;
maxTurns = source.maxTurns;
}
game::~game() {
delete gameBoard; // you needed to have this if you didn't already
delete player_one; // added to release player instance
delete player_two; //added to release player instance
}
// new function swap function (used by assignment operator)
game::swap(game & source) {
std::swap(gameBoard, source.gameBoard);
std::swap(player_one , source.player_one);
std::swap(player_two, source.player_two);
std::swap(turns, source.turns);
std::swap(maxTurns, source.maxTurns);
}
// new function assignment operator
game & operator=(game source)
{
swap(source);
return * this;
}
Look at all the extra stuff you have to write. Odds are there's a bug or two in there.
The assignment operator is taking advantage of the Copy and Swap Idiom. It's often not the fastest way to assign, but it is brutally easy to write and very hard to get wrong. I start with Copy and Swap and use it until life proves that it is a performance bottleneck.
If gameBoard, player_one and player_two are Automatic variables, the compiler will generate the destructor and all of the copying code for you unless board is poorly written. One of your goals should be to make all of your classes that own a resource observe the Rule of Three or Five so that classes that contain them can take advantage of the Rule of Zero.

How do you place an Object inside a Room in a text-based game?

I'm a coding newbie (and despite what my user name may imply I am far from a pro), and I'm trying to write my own text-based adventure game. I have two questions.
First, I want to implement an Object class. These Objects have names and descriptions and can be placed in rooms, as well as picked up and carried around by the player. What's messing me up is that these Objects are supposed to know what room they were originally in, their "homeroom" so to speak.
I'm not sure how to let each Room know that they have Objects placed within them. Everything that I've tried to do has failed to compile.
I've tried to include Room r as a private variable in Object.cpp and include Room to the Object constructor.
Object::Object(string name, string description, Room *r)
{
name_ = name;
description_ = description;
r_ = r; //assume r_ is a private variable
}
Secondly, regarding pointers... This assignment specifies that I must have a vector of pointers of Objects. Would it look like this?
vector<Object*>objectsInRoom;
In main.cpp, I also need a vector of Objects. Is the vector inside the Room class keeping track of Objects in each Room? And is the vector in main.cpp keeping track of all the objects the player carries. Why must the room class have a vector of pointer of Objects? Would not having a vector of Objects suffice?
(I apologize if this sounds vague; this game is based off of an assignment that can be found here. If you scroll down to the "Extra Credit" portion and go to the first paragraph block marked 10 points, you'll find a much more extensive explanation that I tried to condense above.)
room.cpp
// Room.cpp: implementation of the Room class.
//
//////////////////////////////////////////////////////////////////////
#include "Room.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Room::Room()
{
name_ = "The void";
description_ = "There is nothing but blackness in every direction.";
int i;
for(i = 0; i < 4; i++) // set all exits to "closed"
exits_.push_back(NULL);
}
Room::Room(string name, string desc)
{
name_ = name;
description_ = desc;
int i;
for(i = 0; i < 4; i++) // set all exits to "closed"
exits_.push_back(NULL);
}
Room::~Room()
{
cout << "Destroying: " << name_ << endl;
// make sure all exits to this room are
// destroyed so that no one can try to enter
// this room from another location
if(exits_[NORTH] != NULL)
disconnect(NORTH);
if(exits_[EAST] != NULL)
disconnect(EAST);
if(exits_[SOUTH] != NULL)
disconnect(SOUTH);
if(exits_[WEST] != NULL)
disconnect(WEST);
}
// --- inspectors ---
Room * Room::north() const
{
return exits_[NORTH];
}
Room * Room::south() const
{
return exits_[SOUTH];
}
Room * Room::east() const
{
return exits_[EAST];
}
Room * Room::west() const
{
return exits_[WEST];
}
string Room::name() const
{
return name_;
}
string Room::description() const
{
return description_;
}
/*
vector<Object> Room::object() const
{
return roomObjects;
}
*/
// --- mutators ---
void Room::set_name(string n)
{
name_ = n;
}
void Room::set_description(string d)
{
description_ = d;
}
/*
void Room::set_object(Object o)
{
allObjects.push_back(o);
}
*/
// --- facilitators ---
bool Room::connect(Direction exit, Room *r, Direction to)
{
// check that both exits are free
if (exits_[exit] != NULL or r->exits_[to] != NULL)
return false;
// make connection
exits_[exit] = r;
r->exits_[to] = this;
return true;
}
// --- private methods ---
void Room::disconnect(Direction d)
{
// disconnects ALL exits from another
// room to this one. It's sloppy, but
// that's OK.
Room * other_room;
other_room = exits_[d];
int i;
for(i = 0; i < 4; i++) {
if (other_room->exits_[i] == this)
other_room->exits_[i] = NULL;
}
}
// --- operators ---
ostream & operator<<(ostream & out, const Room & r) {
out << r.name() << endl;
out << r.description() << endl;
return out;
}
Object.cpp
#include "Object.h";
Object::Object()
{
name_ = "Object";
description_ = "The object lies in the room";
}
Object::Object(string name, string description)
{
name_ = name;
description_ = description;
}
EDIT: So, If I simply add vector<Object*> allObjects under private attributes in Room.h, I get these enter image description here. Sorry, I'm not allowed to embed images yet.
I'd recommend (trying to adhere to your proposed architecture as much as possible) to define a method void Room::pushObject(Object* argObject). Inside your constructor Object::Object, you could add a call r->pushObject(this). Then you could iterate through your vector once you're inside a room.
Also, a linked list std::deque would be a better solution for your needs, as it is designed for faster insertion and deletion. Then you could room1.insert(room0.erase(yourObjPtr)) to move your objects around.
Note that the answer is theoretical, I did not check whether these compile or not.
Edit 1:
Why must the room class have a vector of pointer of Objects?
You might as well have a vector to the object instance itself, but when you wish to move the object to another room, program would have to copy all the content, instead of passing a single pointer. Also, it would prohibit the use of inheritance (you probably will get to use it in the near future :))
Edit 2:
I also see now that I misinterpreted your design. You intend to use a single global vector. I thought you wanted to use a more "object-oriented" approach, so to speak. I'd put a std::deque for each and every room, but if you wish to keep it this way, regarding your main question
I'm not sure how to let each Room know that they have Objects placed within them.
you might do something (thought inefficiently) like this:
void Room::processMyObjects(void)
{
for (int i = 0; i < globalObjectVector.size(); i++)
{
if (globalObjectVector[i]->r_ == myRoomId)
{
// do whatever you want here.
}
}
}
Also you'd either have to declare r_ public, write a public getter function, or declare friend class Room inside Object declarations this way, otherwise Room cannot see a private variable of Object.
You've got a Many-to-May relationship in this case. A room doesn't own the an object, an object is in a room. You can model Many-to-May relationship with a sparse map. Say, std::unordered_multimap<Room*, Object*>. With this approach you can easily query all objects in a particular room or move object to another room by associating in with different key. You will need a reverse map std::unordered_multimap<Object*, Room*> if you want to know which room contains your object. Also, smart pointers won't help you because you won't create and destroy objects often. It's better to bind objects' lifetime with lifetime of a level they're associated with.

Restarting a game and reinstantiate objects

Introduction
I am creating a small game in C++ and would like to create a function to restart the game.
First I am creating the object player. Then I have an if statement to determine when a certain key is pressed to call the New() method.
My goal
In that method I would like to reinstantiate an object of the Player class, so all variables will be resetted.
My code:
Player player;
//New game method
Game::New()
{
player = new Player();
}
//Game loop
Game::Loop()
{
if(keyispressed(key))
{
Game.New();
}
}
Any suggestions?
You're confusing pointer and non-pointer variables. new Player() returns the address of a dynamically allocated Player object. You cannot assign this address to the non-pointer variable player; you'd need to declare player as a pointer:
Player* player = new Player();
You also need to remember to release the memory previously allocated with a matching delete:
// player starts out pointing to nothing
Player* player = 0;
//New game method
Game::New()
{
// If player already points to something, release that memory
if (player)
delete player;
player = new Player();
}
Now that player is a pointer you'll have to update any other code you've written which uses player, to use the -> member access operator. For example, player.name() will become player->name()