I'm working on a simple console application to prototype a method of calculating battle between two large units of medieval soldiers. The idea is I'll set up the calculations, and run the program several times with different parameters to get a feel for it and if it's what I want. Anyways, on to the problem.
Each of the two units is represented by a Unit class. The class includes variables for "attack", "defense", "tactics", and "unit size" as well as a few others which I'm commenting out and ignoring until I fix this issue. Here's the code for the class header:
#ifndef UNITS_H
#define UNITS_H
#include <string>
#include <iostream>
class Unit
{
public:
int attack;
int defense;
int charge;
int brace;
int tactics;
int unit_size;
public:
bool isAlive;
std::string name;
Unit(std::string name_target)
{
name = name_target;
attack = 1;
defense = 1;
charge = 1;
brace = 1;
tactics = 1;
unit_size = 1;
isAlive = true;
}
float GetAttack() {return (float)attack;}
float GetDefense() {return (float)defense;}
float GetCharge() {return (float)charge;}
float GetBrace() {return (float)brace;}
float GetTactics() {return (float)tactics;}
float GetSize() {return (float)unit_size;}
void SetAttack(int change) {attack = change;}
void SetDefense(int change) {defense = change;}
void SetCharge(int change) {charge = change;}
void SetBrace(int change) {brace = change;}
void SetTactics(int change) {tactics = change;}
void SetSize (int change) {unit_size = change;}
void TakeCasualties (int casualties);
int Defend(Unit enemy, float base_chance, float base_multiplier);
void DisplayStats();
};
#endif // UNITS_H
Sorry if it's sloppy, but I'm a bit of an amateur.
I would include the .cpp code for the class, but everything in there works fine. I've done test runs with just the default values with no issue. The only problem I have is changing the values after they're constructed.
In main.cpp I have a function AssignStats(Unit unit_target) which I call once the two Units are constructed. I call it on each one in turn. The code for that is as follows:
void AssignStats (Unit unit_target)
{
int x;
cout << unit_target.name << endl;
cout << "Value for attack?" << endl;
cin >> x;
cout << x << endl;
unit_target.SetAttack(x);
cout << "Value for defense?" << endl;
cin >> x;
unit_target.SetDefense(x);
//unit_target.SetCharge(x);
//unit_target.SetBrace(x);
cout << "Value for tactics?" << endl;
cin >> x;
unit_target.SetTactics(x);
cout << "Value for size?" << endl;
cin >> x;
unit_target.SetSize(x);
}
As far as I can tell, this code should work. However, when I display the stats of each Unit afterwards, it shows the default values put in by the constructor. For the life of me I can't figure out why my Set functions aren't working. I rewrote the code a little earlier to check if they were getting input right, like so:
void Unit::SetAttack()
{
int x;
std::cout << "Input target value for attack." << std::endl;
std::cin >> x;
std::cout << x;
attack = x;
std::cout << attack;
}
I would input 10, and each of the couts would show me 10 right back, even the one that supposedly displayed the attack variable of the class, but later on when I called DisplayStats(), it would show everything to be at default values again.
Can someone more experienced than me please clear this up?
I think that the problem is that function AssignStats accepts the argument by value
void AssignStats (Unit unit_target);
That is the function deals with a copy of the original object. The original object itself is not changed.
Change it the following way
void AssignStats (Unit &unit_target);
I tested your Set functions and they are working fine. I think the problem is the
void AssignStats (Unit unit_target);
You are passing the Unit parameter by value, not by reference. This causes a local copy of the Unit object to be made, which initializes it with new parameters.
You need to call by reference:
void AssignStats (Unit &unit_target);
That way, a reference to the original object is passed and your Set() function assignments will work on it.
Some additional advice beyond the question:
In C++ passing parameters by reference is usually preferable to passing by value, because the overhead of passing a large object by value due to copying can be substantial.
If you are using setter/getter methods to set parameters, those parameters should be declared private or protected, not public. That's one aspect of object oriented encapsulation - don't allow the parameters to be changed outside the object.
e.g you should have
//or protected if you will subclass Unit later
private:
int attack;
int defense;
int charge;
int brace;
int tactics;
int unit_size;
Related
I am trying to write what I expected to be a very simple program for a text-based game where a player unit and an enemy unit deal damage to each other until one unit hits 0 health. However, somehow both the player and the enemy are resetting to full health between each call of my attack function. This makes no sense to me because the unit health correctly updates in order to print within the attack function. Here is my code. I called the attack function multiple times in my code to demonstrate.
#include <iostream>
using namespace std;
class player; // this is to avoid a syntax error in the enemy attack function
class enemy
{
public:
int health;
int strength;
int defense;
enemy(int h, int s, int d);
void attack(player u);
};
class player
{
public:
int health;
int strength;
int defense;
player(int h, int s, int d);
void attack(enemy u);
};
player::player(int h, int s, int d)
{
health = h;
strength = s;
defense = d;
srand(time(NULL));
cout << "You arrive to the battlefield..." << endl;
}
void player:: attack(enemy u)
{
int damage = 0;
damage = strength - u.defense + (rand() % 3);
u.health -= damage;
cout << "You strike the enemy and deal " << damage << " damage. The enemy now has " << u.health << " health.\n" << endl;
}
enemy::enemy(int h, int s, int d)
{
health = h;
strength = s;
defense = d;
cout << "An enemy has appeared!" << endl;
}
void enemy::attack(player u)
{
int damage = 0;
damage = strength - u.defense + (rand() % 6);
u.health -= damage;
cout << "The enemy strikes you and deals " << damage << " damage. You now have " << u.health << " health.\n" << endl;
}
void battle()
{
player p(50,35,20);// Both healths are set at 50
enemy e(50,34,19);
p.attack(e); // Target health resets after each attack function runs. Unsure why.
//cout << e.health << endl;
e.attack(p);
p.attack(e);
e.attack(p);
p.attack(e);
e.attack(p);
}
int main()
{
battle();
return 0;
}
It's simple enough. Your code is copying your objects and you are altering the hit points of the copied objects, not the original objects in battle. That's why it seems to reset when you go back to battle. You're making the common but incorrect assumption that C++ objects are references, but they are not, they are values and they do get copied.
Change this
void enemy::attack(player u)
to this
void enemy::attack(player& u)
The extra & makes u a reference, and not a copy, so the changes you make will affect the original object, not a copied object. Similar changes needed elsewhere.
Depending on if you only have one copy of the player object vs multiple you can just simply make void enemy::attack() without an argument.
If there's only one player ever, then you can just simply do e.attack() and not pass in the value and reference the player object.
Player could easily be a singleton class (since there's only one copy of it, ever). If there are multiple copies of Player or multiple players then this wouldn't work clearly.
I am trying to call a function in another class. I need to use the surface area function or the information that was stored in it from the previous class for another class. How would I go about that?
I have already tried HalfOpenCylinder::surfaceArea() and HalfOpenCylinder.surfaceArea() and neither worked.
//surface area function that I want to use for other class
double HalfOpenCylinder::surfaceArea(double height, double pi) {
double surfaceArea = (2 * pi * radius * height) + (pi * pow(radius, 2));
return surfaceArea;
}
To call a function from another class you need to first create an object (instance) of that class.
Using that object you can call a particular function
eg:
#include <iostream>
using namespace std;
class Student
{ // defining class
public:
int id;
void add(){
int x=1;
int y=2;
int z=x+y;
cout<<z<<endl;
}
};
int main() {
Student s1; // creating object of class
s1.id=20;
s1.add() // calling function of that class
return 0;
}
I have written a script that shows you how you can
"call a function in another class"
and
"use the surface area function or the information that was stored in it from the previous class for another class".
This is a script with many examples in it. It uses an updated version of your surfaceArea() method (method being the term since the function is defined from within a class). I have also included what output the script produces at the very bottom of the script.
You can copy and past this entire code segment into a C++ compiler and it should work for you. I compiled and tested it in Visual Studio 2015 Community. I went to new project, and created a "Win32 Console Application" in the C++ category.
// ConsoleApplication10.cpp : Defines the entry point for the console application.
//
// This class example was created to answer kittykoder's question on StackOverflow.
// Both of these need to be included
#include "stdafx.h"
#include <iostream>
// We need the std namespace
using namespace std;
// Here I am defining a struct so that you can easily take all the values out of the
// HalfOpenCylinder class at once, and even make a new Cylinder object with the values by
// using the struct in one of the two HalfOpenCylinder class constructors.
struct CylinderValues
{
public:
CylinderValues(double radius, double height, double surfaceArea) {
this->radius = radius;
this->height = height;
this->surfaceArea = surfaceArea;
}
__readonly double radius;
__readonly double height;
__readonly double surfaceArea;
};
// This is the class I saw in your example. Since it is named
// HalfOpenCylinder, I decided to treat it like an
// instantiatable object class, both because it makes sense name wise,
// and based on the context you provided in your question.
class HalfOpenCylinder
{
public:
// Pi is always 3.14, so there is no reason to make it a passable parameter
// like in your example. Thus I have made it a constant. It's a static constant because
// of the static function I've placed in this class to help in answering your question.
static const float pi;
// I have encapsulated the variables that make up this
// class's objects behind methods so that the surface area can be
// updated every time the radius or height values are changed.
double GetRadius() { return radius; }
void SetRadius(double value) { radius = value; UpdateSurfaceArea(); }
double GetHeight() { return height; }
void SetHeight(double value) { height = value; UpdateSurfaceArea(); }
double GetSurfaceArea() { return surfaceArea; }
// You can make a HalfOpenCylinder object with this constructor
HalfOpenCylinder(double radius, double height) {
this->radius = radius;
this->height = height;
UpdateSurfaceArea();
}
// You can use another HalfOpenCylinder object to make a new HalfOpenCylinder object using
// this constructor.
HalfOpenCylinder(CylinderValues values) {
radius = values.radius;
height = values.height;
surfaceArea = values.surfaceArea;
}
// This will return the struct needed to use the constructor just above this comment.
CylinderValues CopyValues() {
return CylinderValues(radius, height, surfaceArea);
}
// Here is your surface area calculation from your question
static double CalculateSurfaceArea(double radius, double height) {
return (2 * pi * radius * height) + (pi * pow(radius, 2));
}
private:
// Here are the values you wanted to be able to access from another class.
// You can access them using the methods above for getting and setting. The
// surfaceArea value is automatically recalculated if you change either the
// radius or height variable's values.
double radius;
double height;
double surfaceArea;
// This method is here so that HalfOpenCylinder objects can use the
// Surface area calculation. I could have copied and pasted the calculation
// code here to avoid calling the static method, but then I would be writing code
// more than need be. This way, you can update one and the other will be correct.
void UpdateSurfaceArea() {
surfaceArea = CalculateSurfaceArea(radius, height);
}
};
// This is honestly just here because the compiler yelled at me for defining a static
// constant inside a non-static class. Could'a gotten away with it in C#. Thank you compiler.
const float HalfOpenCylinder::pi = 3.141592;
// This is called a function since it is outside of any class (although,
// that is one of the few differences between functions and methods.
// Methods being, functions defined inside classes)
void ThisIsAFunction() {
cout << "This is the text from the function named: ThisIsAFunction";
}
// This class is just here to show you how to call functions and methods from inside classes
class CallFunctionAndMethodTester
{
public:
void MethodInsideTheClass() {
cout << "The below is printed from a function called in a class: \n";
// Here, I am calling a function from inside a class
ThisIsAFunction();
cout << "\n\nThe below is printed from a static method called in a class: \n";
// Here, I am calling a static method from inside a class
cout << HalfOpenCylinder::CalculateSurfaceArea(14.5, 50.5);
// Here, I am making an object instance from inside a class
HalfOpenCylinder bobTheCylinder(1.5, 5.4);
cout << "\n\nThe below is printed from an object's method called in a class: \n";
// Here, I am calling an object's method from inside a class
cout << bobTheCylinder.GetRadius();
}
};
// Ok. We made it. THIS main function is where we will use and
// test the classes we have made above.
int main() {
// Make a new cylinder object. No pointer, so it will be destroyed when the computer
// reads past main (which is the end of this program anyways).
cout << "Cylinder 1 Values: \n";
HalfOpenCylinder cylinder1(5.0, 10.0);
cout << cylinder1.GetRadius();
cout << "\n"; // <--just makin' a newline here
cout << cylinder1.GetHeight();
cout << "\n";
cout << cylinder1.GetSurfaceArea();
cout << "\n\n"; // <--just makin' two newlines here
// Change the object's height. The surface area updates automatically.
cout << "Cylinder 1 new surface area once Height is changed: \n";
cylinder1.SetHeight(20.5);
cout << cylinder1.GetSurfaceArea();
cout << "\n\n";
// Make a second Cylinder using the first cylinder's values.
cout << "Cylinder 2 Values: \n";
HalfOpenCylinder cylinder2(cylinder1.CopyValues());
cout << cylinder2.GetRadius();
cout << "\n";
cout << cylinder2.GetHeight();
cout << "\n";
cout << cylinder2.GetSurfaceArea();
cout << "\n\n";
// Here I'm using the static CalculateSurfaceArea function to use the surface area
// method without having to make a new HalfOpenCylinder object.
cout << HalfOpenCylinder::CalculateSurfaceArea(5.0, 10.0);
cout << "\n\n";
// Here I am making an object of type CallFunctionAndMethodTester so that I can call
// the method inside it that is using my example of how to call functions and methods
// from within classes.
CallFunctionAndMethodTester tester;
cout << "Everything printed to the console after this line is printed using functions and methods that are called from inside classes. \n\n";
tester.MethodInsideTheClass();
int meh;
cin >> meh;
return 0;
}
/* Here is the output of this code when the program runs:
Cylinder 1 Values:
5
10
392.699
Cylinder 1 new surface area once Height is changed:
722.566
Cylinder 2 Values:
5
20.5
722.566
392.699
Everything printed to the console after this line is printed using functions and methods that are called from inside classes.
The below is printed from a function called in a class:
This is the text from the function named: ThisIsAFunction
The below is printed from a static method called in a class:
5261.38
The below is printed from an object's method called in a class:
1.5
*/
I'm new and just learning C++ and came across this problem that I've spent maybe an hour trying to fix and researching answers on but I cant seem to figure out what I'm doing wrong. I'm using Visual Studios as my IDE, the most recent version.
#include "stdafx.h"
#include <iostream>
#include "constant.h"
//height of the tower
double towerHeight(double x)
{
using namespace std;
cout << "Enter a height for the tower" << '\n';
cin >> x;
return x;
}
//the number of seconds since the ball has been dropped to determine the distance
double secondsSinceDrop(double x)
{
using namespace std;
cout << "How long has it been since you dropped the ball (Seconds): ";
cin >> x;
return x;
}
//finds how far off the ground the ball is
double currentBallHeight(double x, double y)
{
return y * constant::gravity - x;
}
//prints how far off the ground the ball is
void printResult(double x, double y)
{
using namespace std;
if (currentBallHeight(x, y) < 0)
cout << "At " << y << " the ball is on the ground." << '\n';
else
cout << "At " << y << " the ball is at: " << currentBallHeight(x, y) << '\n';
}
int main()
{
double x = towerHeight(x);
double y = secondsSinceDrop(x);
printResult(x,y);
return 0;
}
This is the Error Code
- chapter 2 comprehensive quiz (part 2).cpp(46): error C4700: uninitialized local variable 'x' used
-Line (46) is - double x = towerHeight(x);
I've been getting this and I've changed my code around to get it down to just this one error but i cant figure out how to fix it. Its probably something simple and I'm dumb for overlooking it but any help would be greatly appreciated.
These lines will be throwing errors
because the variable 'x' you are sending as an argument does not exist in the scope of main
int main()
{
-> double x = towerHeight(x);
-> double y = secondsSinceDrop(x);
printResult(x,y);
return 0;
}
Instead you could try something like this.
#include "stdafx.h"
#include <iostream>
#include "constant.h"
using namespace std;
//height of the tower
double towerHeight()
{
double height;
cout << "Enter a height for the tower" << '\n';
cin >> height
return height;
}
//the number of seconds since the ball has been dropped to determine the distance
double secondsSinceDrop()
{
double seconds;
cout << "How long has it been since you dropped the ball (Seconds): ";
cin >> seconds;
return seconds;
}
//finds how far off the ground the ball is
double currentBallHeight(double x, double y)
{
return y * constant::gravity - x;
}
//prints how far off the ground the ball is
void printResult(double x, double y)
{
if (currentBallHeight(x, y) < 0)
cout << "At " << y << " the ball is on the ground." << '\n';
else
cout << "At " << y << " the ball is at: " << currentBallHeight(x, y) << '\n';
}
int main()
{
double height = towerHeight();
double seconds = secondsSinceDrop();
printResult(height, seconds);
return 0;
}
Some tips that I would recommend
Declare your variables as much as relevant to you instead of using 'x/y/z'
There is no need to add the using namespace std; inside each function
Your first line of code in main() is double x = towerHeight(x);, what value of x are you sending to the function, when you have not initialized it.
When you are using a variable without initializing the value of it is undefined.
You can pass the variable as a reference to your function and accept the values inside it.
//height of the tower
void towerHeight(double &x)
{
using namespace std;
cout << "Enter a height for the tower" << '\n';
cin >> x;
}
//the number of seconds since the ball has been dropped to determine the distance
void secondsSinceDrop(double &y)
{
using namespace std;
cout << "How long has it been since you dropped the ball (Seconds): ";
cin >> y;
}
int main()
{
double x = 0.0, y = 0.0;
towerHeight(x);
secondsSinceDrop(y);
printResult(x, y);
return 0;
}
You seem to be struggling to connect the mental dots on what the computer is doing when you
declare variables with an initial value
define function parameters
return a value from a function
Not sure how this question will fair with the SO community as the preference is for Q/A that is succinct and reusable (maybe some editing can help) but for your benefit let me explain some of these concepts.
Let's start with a variable declaration
int x = 5;
int y = x;
When you define int x; it creates a space in RAM for an integer (4 bytes). Adding the = 5 initializes it immediately. It's important that the value on the right side of = (5 in this case) is known before the computer tries to make space for x.
It's fine to use values that aren't constant for variables like this (notice the second line in the example) but x has to be known before you declare y. In other words, this would obviously be a problem:
int y = x;
int x = 5;
For this same reason, the line: double x = towerHeight(x); is problematic because you're using x when you call towerHeight before ever defining x
When you define a function's parameters:
double towerHeight(double x) {
This tells the computer that you are going to copy the value from whatever called towerHeight to a new place in RAM and call it "x". This means that the value outside of the function doesn't get modified. Consider the following example:
double towerHeight(double x) {
x = 5;
std::cout << x << std::endl; // outputs 5
}
int main() {
double x = 10;
towerHeight(x);
std::cout << x << std::endl; // outputs 10
return 0;
}
Even though x was changed in towerHeight that was a "different copy of x" which also happened to be called the same name.
When you return a value from a function, in the same manner as passing a function argument, the return value is copied and used in places of the function call. Let's modify the previous example slightly:
double towerHeight(double x) {
x = 5;
return x;
}
int main() {
double x = 10;
x = towerHeight(x); // returns the value "5"
std::cout << x << std::endl; // Outputs "5"
return 0;
}
You can think of towerHeight(x) being replaced by "5" so the code would read x = 5;
Conclusion
You should try and use different variable names for
function arguments (the variables/values you pass to the function)
function parameters (what they are called inside the function)
to avoid this kind of confusion. Though there may be times where using the same name makes sense (i.e. passing by reference, which is another question). It's important for you to be aware of what's really going on.
Here is what you probably intend to do:
double towerHeight()
{
double height;
std::cout << "Enter a height for the tower" << std::endl;
std::cin >> height;
return height;
}
double secondsSinceDrop()
{
double seconds;
std::cout << "How long has it been since you dropped the ball (Seconds): ";
std::cin >> seconds;
return seconds;
}
double currentBallHeight(double y0, double t)
{
return y0 - (constant::gravity * t * t / 2);
}
void printResult(double y0, double t)
{
double currentHeight = currentBallHeight(y0, t);
if (currentHeight < 0)
std::cout << "At " << t << "s the ball is on the ground." << std::endl;
else
std::cout << "At " << t << "s the ball is at: " << currentHeight << std::endl;
}
int main()
{
double y0 = towerHeight();
double t = secondsSinceDrop();
printResult(y0, t);
return 0;
}
Summarizing what I've changed:
Renamed x to y0 since y(0)/h(0) is typically used for "initial height" in physics classes, and similarly y with t (though time would be an even better name).
Don't pass anything to towerHeight or secondsSinceDrop; you're not trying to give those functions something, you're trying to get something out of them.
Move the definition of x from a function parameter to a local variable defined in the function for towerHeight and secondsSinceDrop
Removed the duplicated call to currentBallHeight (no need to do the same math twice, it takes time to crunch numbers after all, however small in this case)
Rewrote for proper usage of std::cout and std::endl
Rewrote the currentBallHeight equation to match constant free-fall kinematics (y(t) = y(0) - 0.5g * t^2) as an added bonus (assuming constant::gravity > 0)
At some point it will be valuable for you to become aware of the more technical terminology and definitions for the concepts I've outlined here. Here are some recommended readings (just to get you started; keep learning, always):
Sequence Points
Parameters and Arguments
Passing by Reference vs by Value
Passing pointers vs by Reference
Making sure you understand what using namespace std; does and why you should never use it
Rewrite your function as following:
//height of the tower
double towerHeight()
{
double x;
using namespace std;
cout << "Enter a height for the tower" << '\n';
cin >> x;
return x;
}
and in int main(){} rewrite following line:
double x = towerHeight();
I guess this will do but you can actually modify your double secondsSinceDrop(double x); function this way as it doesn't really need a double value as parameter.
I am just started learning OOP concepts and to help myself learning, I have created a Characters class. From this class I have made instance called main and an instance called monster. Here is the code for the class:
#include <iostream>
#include <string>
using namespace std;
class Character {
public:
string name;
float health;
int attackLevel;
int defenseLevel;
void setAttr(string sName,float sHealth, int sAttackLevel, int sDefenseLevel) {
name = sName;
health = sHealth;
attackLevel = sAttackLevel;
defenseLevel = sDefenseLevel;
}
void attack(int whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
return whatInstanceToAttack;
}
int defend(string defend) {
int damageRelieved = defenseLevel * 2;
return damageRelieved;
}
};
int main() {
Character main;
Character monster;
main.setAttr("Rafael",200,100,30);
monster.setAttr("Monster1",30,40,30);
cout << "Default Values for Raf are;" << endl;
cout << main.name << endl;
cout << main.health<< endl;
cout << main.attackLevel << endl;
cout << main.defenseLevel << endl;
cout << "Default values for monster are" << endl;
cout <<monster.name << endl;
cout <<monster.health << endl;
cout << monster.attackLevel<< endl;
cout << monster.defenseLevel << endl;
return 0;
}
Basically what I want to do is somehow access the monster instance via the main instance. I want to do this by running the attack method. So if I run
main.attack(monster);
then I want the monster to lose 20 hitpoints.
How do I go about doing this?
All you need is to pass reference of Character in attack method.
I think you must be aware of pass by value and pass by reference concept. If not you can read it here
void attack(Character &whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
}
Yes you can access the variables of an instance from another instance of the same class. You need to use a reference to the object to ensure the changes are reflected in the other instance. So here is what your attack function should look like.
void attack(Character &c)
{
c.hitpoints - = 20;
}
Now when you call main.attack(monster) from the main() function, the hitpoints of monster will get decremented by 20.
As a side note, it is considered a good practice to make the data members of a class private, to avoid illegal access/modification of the data. Always use the member functions as an interface to your class instances.
overload the method attack and you can pass by value or reference as per your requirement.
void attack(Character chr)
or
void attack(Character &chr)
#include<iostream>
#include<conio.h>
class Number
{
private:
int x, y;
public:
Number()
{
x = y = 100;
}
void avg()
{
std::cout<<"x = "<<std::cout<<x;
std::cout<<std::endl;
std::cout<<"Y = "<<std::cout<<y;
std::cout<<std::endl;
std::cout<<"Average = "<<std::cout<<(x+y)/2;
}
};
main()
{
Number n;
n.avg();
}
This programme runs but shows wrong answer, may be showing addresses of memory locations instead of showing the assigned values of 100. Please correct me why it is behaving like this?
std::cout << "x = " << std::cout << x;
is wrong. You need
std::cout << "x = " << x;
Otherwise, the std::cout stream object in ...<< std::cout is implicitly converted to a (void*) when invoking operator<< on it, and therefore the pointer (an address) is displayed.
The conversion to void* exists for historic reasons (the safe bool idiom), but in C++11 was removed, due to the introduction of explicit conversion operators, so your code should not compile in C++11.