Forgive me if the title isn't specific enough. Let's say I want to make an RPG. I make a class for the characters. I then make an array that function as a party of characters. Then I have a function that reduced the HP of the first member of a party.
`
#include <iostream>
#include <string>
class Chara
{
public:
int HP;
Chara(int health)
{
HP = health;
}
};
int Battle(Chara Party[2])
{
Party[0].HP -= 2;
}
int main()
{
Chara Final(0);
Chara Fantasy(7);
Chara Player[2] = {Final, Fantasy};
Battle(Player);
std::cout << Final.HP;
}
`
However, the HP of the character doesn't change. Is there something that I can do to make it so that the character's HP actually changes?
You have two separate problems caused by C++ passing structs by copy.
First, the line Chara Player[2] = {Final, Fantasy}; creates an array of Chara and initializes the members with copies of the mentioned variables. That means that the final line will not see any modifications made to elements of Player.
Instead, you should do:
Chara Player[2] = { Chara{0}, Chara{7} };
// and optionally, if you still want to access individual members:
Chara& Final = Player[0];
Chara& Fantasy = Player[1];
Secondly, you pass Player to the Battle function by copy. Thus, any changes made to Party inside the function are not reflected in the outer Player variable.
The quick fix is to take Party by pointer instead:
void Battle(Chara* Party) { ... }
This works because arrays can decay to pointers to their first element when passed.
As best practice, you should probably use a std::vector instead, which allows you to dynamically add and remove party members and has all kinds of helpful methods.
Related
I'm new to C++ and having a bit of trouble with accessing variables inside a class. From what I've read on here so far creating globals is a really bad practice and not to do that, but I dont know how else to move access to a class around.
So far my searches have pointed me to set and get functions in the class, but I think I can only use them in the block where the object is defined.
Basically what I want to know is if I define a class object in main() then call a function in main like gameLoop() how do I access that object inside that new function without making the class object global.
For instance:
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <string>
class Word
{
private:
string m_word;
int m_length;
public:
void set(string word, int length)
{
m_word = word;
m_length = length;
}
};
void gameLoop()
{
word1.set(); //flags error as it cant acces the word1 object
//I want to be able to access word1 from here
//Not a copy because that wouldnt change the actual word1
//I dont want to define it in here because then it would be created again
//for each loop of gameLoop
}
int main()
{
Word word1;
int play = 1;
while (play ==1){
gameLoop();
}
return 0;
}
This is a largely simplified version but for the purpose of the game I want the class to be stored externally but for a number of gameplay functions inside gameLoop to be able to access and change the class object.
Ideally, the dependencies of a function should be stater in the parameter list. If your gameLoop function requires a Word object, pass it in as an argument. That way, it's clear what objects the function requires to work. This would look something like:
void gameLoop(Word& word)
{
word.set();
// ^ Obviously you need to supply args here.
}
int main()
{
Word word1;
int play = 1;
while (play ==1){
gameLoop(word1);
}
return 0;
}
And since you're mutating the Word object in the function, you need to pass it by reference, or else you're just modifying a copy.
Your only other sane option would be to make the word object global, but that should be avoided at all costs. It makes testing more difficult since you need to take into consideration every change that may have happened, which makes it difficult to isolate problems.
If you're structuring you're program this way, you would have to pass it as a parameter to gameloop
However, This is a very poor design, since anything you create in main and want to access in gameloop would have to be passed in as an argument. I would consider a different approach.
You could move the entire loop and variable declarations into gameLoop as so:
void gameLoop()
{
Word word1;
int play = 1;
while (play ==1){
word1.set(/*args*/);
}
}
int main()
{
gameLoop();
return 0;
}
This way you won't have to pass a million things into gameloop. Generally in games there's some initialization and finalization that needs to take place as well, and this can all be done in main before/after the call to gameLoop.
So to answer you original question, you would have to pass word1 as an argument to gameLoop(). So redefine your function to accept a Word argument, and then pass in word1. but again, I would reconsider your current design before its too late
I am creating a Pokémon Battle Simulator, and I want to know if I can call a class by using an element of an array.
#include <iostream>
#include <time.h>
#include <string>
using std::cout;
using std::endl;
using std::string;
string PokémonArray[] = { "Pikachu","Pidgey" };
class Pokémon {
public:
string basic_attack;
int basic_attack_dmg;
string getBasicAttackName() { return basic_attack; }
Pokémon() { ; }
};
class Pikachu : public Pokémon {
public:
Pikachu(){ basic_attack = "Whatever"; }
};
int main(){
int random_num;
string randEnemy;
srand(TIME(NULL));
random_num = rand() % 2; //Generates a random number between 0 and 1
randEnemy = PokémonArray[random_num]; //Sets randEnemy to be equal to the element 0 or 1 (generated above) of the array
(randEnemy) enemy; //Try to create the object 'enemy' calling a class using an element of the array
}
How can I call the class by using an element of an array that has the same name?
To answer your question directly - yes you can, but you will need lots of if/else statements and you will have to use the polymorphic base.
Pokemon* pokemon = nullptr;
if(randEnemy == "Pikachu")
pokemon = new Pikachu;
else if (randEnemy == "Raichu")
pokemon = new Raichu;
else if...
This pattern is called "Factory method" or "virtual constructor".
You can (and should) store the Pokemon in the array directly, but it will not be so simple as the other answer specifies, as apparently the Pokemon instances are polymorphic. So what you need to store is a pointer to the Pokemon instance.
At the best a smart pointer (for automatic destruction). Depending on the use, it can be either:
std::vector<std::unique_ptr<Pokemon> > PokemonArray;
or
std::vector<std::shared_ptr<Pokemon> > PokemonArray;
(depending whether or not the pointer instance can be owned by multiple owners)
Or a simple array, but I generally prefer the std::vector.
You could store the Pokemon in your array.
Pokemon PokémonArray[152];
And then just call their function directly once you need them.
randEnemy = PokémonArray[random_num];
randEnemy.basicAttack();
I am trying to make a Tic-Tac-Toe game using OOP but I am having an issue. When trying to make a playing board with a total of 9 squares and all of them empty as a vector, I do the following.
Main
#include "stdafx.h"
#include <vector>
int main()
{
char empty = ' '; //Empty square on playing board
const int numbOfSquares = 9; //Total amount of squares on board
std::vector<char> board(numbOfSquares, empty); // The playing board
return 0;
}
In my board class I am trying to do the same thing but it isn't working in the same way.
Board.h
#pragma once
#include <vector>
class Board
{
private:
const char empty = ' '; //Empty square on game board
const int numbOfSquares = 9; //Number of squares on the board
std::vector<char> board(numbOfSquares, empty); //The playing board
public:
};
An error occurs in saying that 'numbOfSquares' and 'empty' is not a type name. I think I understand this error message but I am not sure on how to resolve it. Could I - overload, is that the term - the board variable in a member function instead?
I am left quite clueless on what to do and would love some help. Thank you for your time.
std::vector<char> board(numbOfSquares, empty); is not permitted when specifying the list of what your class members are. Instead you should use the constructor initializer list:
Board(): board(numbOfSquares, empty)
{
}
All members can be initialized this way. For example, your line const int numbOfSquares = 9; is a shortcut for writing:
Board(): numbOfSquares(9)
{
}
However there is no such shortcut for the case where you need to provide constructor arguments in parentheses.
There is a shortcut for providing constructor arguments as a braced initializer list, however it's wise to avoid doing this for vector as the vector will prefer to treat the contents of the braces as a list of initial values for the vector, rather than as constructor parameters.
Given two classes for example Equation and Term, where the Equation has a member vector of type Term
Term.h
class Term
{
public:
Term();
Term(string inputTerm);
private:
string termValue;
}
Term.cpp
#include "Term.h"
Term::Term()
{
//default ctr
}
Term::Term(string inputTerm)
{
termValue = inputTerm;
}
Equation.h
#include 'Term.h'
class Equation
{
public:
Equation(string inputEQ)
private:
vector<string> termStrings; //The input for the constructors of a Term
vector<Term> theTerms; // The vector I wish to populate
}
Equation.cpp
#include "Equation.h"
Equation::Equation(string inputEQ)
{
while(parsing inputEQ for individual terms)
{
//This is where the vector of strings is populated
}
for(int i = 0; i < termStrings; i++)
{
//Loop the same number of times as number of terms
//This is where I wish to "push_back" an instance
//of a term with the according termString value to
//my vector "theTerms" however I recieve an error
//when I attempt this.
//updated:
Term * aTerm = new Term( termString[i] );
theTerms.push_back( aTerm );
}
}
What is the most logical way to populate the vector of terms in a loopable method?
I suspect the problem is that your vector is containing concrete objects, and Term does not have a copy or move constructor. You have two options for a solution:
Use vector<shared_ptr<Term>> instead. This will store pointers to your Term objects and allow you to directly manipulate items in your vector.
Add a copy constructor Term::Term(Term&) to your Term class. This will allow items to be copied into and out of your vector.
Which approach is appropriate for your scenario will depend on how you use the Term objects. If you need to pass around instances, then the first approach is the one you want. If you want to make sure items in your list are invariant so you need to work on copies, then the second approach is the right one.
Consider the following class member:
std::vector<sim_mob::Lane *> IncomingLanes_;
the above container shall store the pointer to some if my Lane objects. I don't want the subroutins using this variable as argument, to be able to modify Lane objects.
At the same time, I don't know where to put 'const' keyword that does not stop me from populating the container.
could you please help me with this?
thank you and regards
vahid
Edit:
Based on the answers i got so far(Many Thanks to them all) Suppose this sample:
#include <vector>
#include<iostream>
using namespace std;
class Lane
{
private:
int a;
public:
Lane(int h):a(h){}
void setA(int a_)
{
a=a_;
}
void printLane()
{
std::cout << a << std::endl;
}
};
class B
{
public:
vector< Lane const *> IncomingLanes;
void addLane(Lane *l)
{
IncomingLanes.push_back(l);
}
};
int main()
{
Lane l1(1);
Lane l2(2);
B b;
b.addLane(&l1);
b.addLane(&l2);
b.IncomingLanes.at(1)->printLane();
b.IncomingLanes.at(1)->setA(12);
return 1;
}
What I meant was:
b.IncomingLanes.at(1)->printLane()
should work on IncomingLanes with no problem AND
b.IncomingLanes.at(1)->setA(12)
should not be allowed.(In th above example none of the two mentioned methods work!)
Beside solving the problem, I am loking for good programming practice also. So if you think there is a solution to the above problem but in a bad way, plase let us all know.
Thaks agian
A detour first: Use a smart pointer such shared_ptr and not raw pointers within your container. This would make your life a lot easy down the line.
Typically, what you are looking for is called design-const i.e. functions which do not modify their arguments. This, you achieve, by passing arguments via const-reference. Also, if it is a member function make the function const (i.e. this becomes const within the scope of this function and thus you cannot use this to write to the members).
Without knowing more about your class it would be difficult to advise you to use a container of const-references to lanes. That would make inserting lane objects difficult -- a one-time affair, possible only via initializer lists in the ctor(s).
A few must reads:
The whole of FAQ 18
Sutter on const-correctness
Edit: code sample:
#include <vector>
#include <iostream>
//using namespace std; I'd rather type the 5 characters
// This is almost redundant under the current circumstance
#include <vector>
#include <iostream>
#include <memory>
//using namespace std; I'd rather type the 5 characters
// This is almost redundant under the current circumstance
class Lane
{
private:
int a;
public:
Lane(int h):a(h){}
void setA(int a_) // do you need this?
{
a=a_;
}
void printLane() const // design-const
{
std::cout << a << std::endl;
}
};
class B
{
// be consistent with namespace qualification
std::vector< Lane const * > IncomingLanes; // don't expose impl. details
public:
void addLane(Lane const& l) // who's responsible for freeing `l'?
{
IncomingLanes.push_back(&l); // would change
}
void printLane(size_t index) const
{
#ifdef _DEBUG
IncomingLanes.at( index )->printLane();
#else
IncomingLanes[ index ]->printLane();
#endif
}
};
int main()
{
Lane l1(1);
Lane l2(2);
B b;
b.addLane(l1);
b.addLane(l2);
//b.IncomingLanes.at(1)->printLane(); // this is bad
//b.IncomingLanes.at(1)->setA(12); // this is bad
b.printLane(1);
return 1;
}
Also, as Matthieu M. suggested:
shared ownership is more complicated because it becomes difficult to
tell who really owns the object and when it will be released (and
that's on top of the performance overhead). So unique_ptr should be
the default choice, and shared_ptr a last resort.
Note that unique_ptrs may require you to move them using std::move. I am updating the example to use pointer to const Lane (a simpler interface to get started with).
You can do it this way:
std::vector<const sim_mob::Lane *> IncomingLanes_;
Or this way:
std::vector<sim_mob::Lane const *> IncomingLanes_;
In C/C++, const typename * and typename const * are identical in meaning.
Updated to address updated question:
If really all you need to do is
b.IncomingLanes.at(1)->printLane()
then you just have to declare printLane like this:
void printLane() const // Tell compiler that printLane doesn't change this
{
std::cout << a << std::endl;
}
I suspect that you want the object to be able to modify the elements (i.e., you don't want the elements to truly be const). Instead, you want nonmember functions to only get read-only access to the std::vector (i.e., you want to prohibit changes from outside the object).
As such, I wouldn't put const anywhere on IncomingLanes_. Instead, I would expose IncomingLanes_ as a pair of std::vector<sim_mob::Lane *>::const_iterators (through methods called something like GetIncomingLanesBegin() and GetIncomingLanesEnd()).
you may declare it like:
std::vector<const sim_mob::Lane *> IncomingLanes_;
you will be able to add, or remove item from array, but you want be able to change item see bellow
IncomingLanes_.push_back(someLine); // Ok
IncomingLanes_[0] = someLine; //error
IncomingLanes_[0]->some_meber = someting; //error
IncomingLanes_.erase(IncomingLanes_.end()); //OK
IncomingLanes_[0]->nonConstMethod(); //error
If you don't want other routines to modify IncomingLanes, but you do want to be able to modify it yourself, just use const in the function declarations that you call.
Or if you don't have control over the functions, when they're external, don't give them access to IncomingLanes directly. Make IncomingLanes private and provide a const getter for it.
I don't think what you want is possible without making the pointers stored in the vector const as well.
const std::vector<sim_mob::Lane*> // means the vector is const, not the pointer within it
std::vector<const sim_mob::Lane*> // means no one can modify the data pointed at.
At best, the second version does what you want but you will have this construct throughout your code where ever you do want to modify the data:
const_cast<sim_mob::Lane*>(theVector[i])->non_const_method();
Have you considered a different class hierarchy where sim_mob::Lane's public interface is const and sim_mob::Really_Lane contains the non-const interfaces. Then users of the vector cannot be sure a "Lane" object is "real" without using dynamic_cast?
Before we get to const goodness, you should first use encapsulation.
Do not expose the vector to the external world, and it will become much easier.
A weak (*) encapsulation here is sufficient:
class B {
public:
std::vector<Lane> const& getIncomingLanes() const { return incomingLanes; }
void addLane(Lane l) { incomlingLanes.push_back(l); }
private:
std::vector<Lane> incomingLanes;
};
The above is simplissime, and yet achieves the goal:
clients of the class cannot modify the vector itself
clients of the class cannot modify the vector content (Lane instances)
and of course, the class can access the vector content fully and modify it at will.
Your new main routine becomes:
int main()
{
Lane l1(1);
Lane l2(2);
B b;
b.addLane(l1);
b.addLane(l2);
b.getIncomingLanes().at(1).printLane();
b.getIncomingLanes().at(1).setA(12); // expected-error\
// { passing ‘const Lane’ as ‘this’ argument of
// ‘void Lane::setA(int)’ discards qualifiers }
return 1;
}
(*) This is weak in the sense that even though the attribute itself is not exposed, because we give a reference to it to the external world in practice clients are not really shielded.