How can I best decouple two classes, which one way dependencies - c++

I am attempting to create a D&D combat encounter simulator using C++ and since it's D&D, all aspects of the simulation are going to depend heavily on the "Dice" Class and its methods.
I could instantiate a "Dice" object every time another class needs
to invoke its methods, however, that would leave everything heavily
coupled and make it very difficult to make changes or extensions later.
I don't have any practical knowledge about things such as Factories, dependency injections, and other such methods.
My question therefore in essence is:
What would be the best way to ensure that the "Dice" class remains as
decoupled as possible from all other classes?
While still enabling them to make use of the "Dice" objects, and its methods, when needed.
Dice.h
#ifndef dice_h_
#define dice_h_
#include <stdlib.h>
class Dice
{
private:
int maxValue;
public:
Dice(int maxValue);
~Dice();
int getMaxValue( void ){return maxValue;}
void setMaxValue(int newMaxValue){maxValue = newMaxValue;}
int rollDice();
int rollMultipleDice(int numberOfDiceRolls);
};
#endif
Dice.cpp
#ifndef dice_cpp_
#define dice_cpp_
#include "dice.h"
Dice::Dice(int maxValue){this->maxValue = maxValue;}
Dice::~Dice(){}
int Dice::rollDice()
{
return (rand() % maxValue) + 1;
}
int Dice::rollMultipleDice(int numberOfDiceRolls)
{
int i = numberOfDiceRolls, sum = 0;
while(i-- > 0)
{
sum += rollDice();
}
return sum;
}
#endif
Actor.h
#ifndef actor_h_
#define actor_h_
#include "dice.h"
class Actor
{
private:
unsigned int hp;
unsigned int ac; // Armor Class
unsigned int dmg;
public:
Actor(unsigned int hp, unsigned int ac, unsigned int dmg);
~Actor();
unsigned int getHP( void );
unsigned int getAC( void );
unsigned int getDmg( void );
void setHP( unsigned int newHP);
void setAC( unsigned int newAC);
void setDmg( unsigned int newDmg);
void attack(Actor* target);
bool isHit(Actor target);
};
#endif
Actor.cpp
#ifndef actor_cpp_
#define actor_cpp_
#include "actor.h"
Actor::Actor(unsigned int hp, unsigned int ac, unsigned int dmg)
{
this->hp = hp;
this->ac = ac;
this->dmg = dmg;
}
Actor::~Actor(){}
unsigned int Actor::getHP( void ){return hp;}
unsigned int Actor::getAC( void ){return ac;}
unsigned int Actor::getDmg( void ){return dmg;}
void Actor::setHP( unsigned int newHP ){this->hp = newHP;}
void Actor::setAC( unsigned int newAC ){this->ac = newAC;}
void Actor::setDmg( unsigned int newDmg ){this->dmg = newDmg;}
void Actor::attack(Actor* target)
{
Dice damageDice(8);
if (isHit(*target))
{
target->setHP(target->getHP() - damageDice.rollDice());
}
}
// helper function to attack function
// do not use elsewhere
bool Actor::isHit(Actor target)
{
Dice atkDice(20);
return atkDice.rollDice() >= target.getAC();
}
#endif

You talk about singletons and the like in the question so do you want ever class that needs to use the dice to use the same instance of the Dice class?
If so since the Dice class doesn't have any states to hold onto it can be a static class. I would then remove the MaxValue and add an input to the RollDice and RollMutiDice to take a Max Value (which I am guessing is suppose to the "sides" of the dice). So a call to Dice::RollDice(3) is to roll a 3 sided dice or Dice::RollMutiDice(6,3) would roll a 6 sided dice 3 times.
Also make sure you send the rand() class. I think is it srand(int). Usually you pass it time so it is fairly random

Related

C++ Object declaration needs another object as parameter

Object-oriented C++ here.
I'm supposed to code a Microwave object that "heats" a FrozenMeal object.
One method of the Microwave object, called void heatMeal(FrozenMeal), is supposed to take an instance of a FrozenMeal object as a parameter and increase its temperature.
FrozenMeal.h
#include <string>
class FrozenMeal {
public:
FrozenMeal(std::string, int);
void setTemperature(double);
std::string getName() const;
int getVolume() const;
double getCoeffizient() const;
double getTemperature() const;
private:
std::string name;
int volume;
double temperature;
double coeffizient;
};
FrozenMeal.cpp
#include <string>
#include "FrozenMeal.h"
using namespace std;
FrozenMeal::FrozenMeal(string mealName, int mealVolu) {
name = mealName;
volume = mealVolu;
temperature = -18;
coeffizient = 0.24;
}
void FrozenMeal::setTemperature(double mealTemp) { temperature = mealTemp; }
string FrozenMeal::getName() const { return name; }
int FrozenMeal::getVolume() const { return volume; }
double FrozenMeal::getCoeffizient() const { return coeffizient; }
double FrozenMeal::getTemperature() const { return temperature; }
Microwave.h
#include "FrozenMeal.h"
class Microwave {
public:
Microwave();
void morePower();
void lessPower();
void setPeriod(double);
void heatMeal(FrozenMeal); // <----------------------------
int getPower() const;
double getPeriod() const;
private:
int power;
double period;
};
Microwave.cpp
#include "Microwave.h"
using namespace std;
Microwave::Microwave() {}
void Microwave::morePower() { if (power < 1000) power += 200; }
void Microwave::lessPower() { if (power > 200) power -= 200; }
void Microwave::setPeriod(double sessionPeri) { period = sessionPeri; }
void Microwave::heatMeal(FrozenMeal mealInst) {
mealInst.setTemperature(80); //example
}
int Microwave::getPower() const { return power; }
double Microwave::getPeriod() const { return period; }
Now, my problem is that my compiler says that the file FrozenMeal.h apparently redefines the object type of FrozenMeal, even though that should be the job of the FrozenMeal.cpp file, and compiling is unsuccessful.
I tried including FrozenMeal.h to Microwave.cpp but that resulted in even more compiler errors.
I feel like I'm doing something horribly wrong here.
Add include guards to your header files so its contents doesn't get included more than once:
FrozenMeal.h:
#ifndef FROZENMEAL_H_INCLUDED
#define FROZENMEAL_H_INCLUDED
// your code ...
#endif /* FROZENMEAL_H_INCLUDED */
Microwave.h:
#ifndef MICROWAVE_H_INCLUDED
#define MICROWAVE_H_INCLUDED
// your code ...
#endif /* MICROWAVE_H_INCLUDED */
Also, you never initialize int Microwave::power and double Microwave::period so you will read and write garbage values in Microwave::morePower() and Microwave::lessPower()
As suggested in the comments, you want to take the parameter of Microwave::heatMeal() by reference so the function can modify the passed object:
void Microwave::heatMeal(FrozenMeal &mealInst)
// ^

C++ RPG Error: data member initializer is not allowed

this has already been posted several times, but none of the times have answered my case. Please help me with my 'error: data member initializer is not allowed' which appears under the equals signs. Here's the code with the problem in it.
//Player.cpp :Contains information about the player
#include <iostream>
#include <string>
#include "Main.cpp"
using namespace std;
void Player()
{
struct Player {
int Charma = 0;
unsigned int Hunger = 10;
unsigned int Energy = 50;
unsigned int Health = 100;
};
enum Race {
UNKNOWN,
DEAD,
HUMAN,
ORC,
GOBLIN,
ELF,
LIZARD,
CAT,
VAMPIRE,
WEREWOLF,
SNK
};
}
You are getting that error because you are initializing the variables when you are declaring a struct. This is not allowed. Instead, move the initialization into the constructor of the struct.
However that is not the only error in your code. You are defining the struct inside of the Player function (which should be the constructor). You need to switch those, so that you have the Player function inside of the Player struct. This way the struct will have a constructor where you can initialize the values. Another thing, don't #include .cpp files. It's a bad practice.
Your code should be something like this:
struct Player {
int Charma;
unsigned int Hunger;
unsigned int Energy;
unsigned int Health;
Player() : Charma(0), Hunger(10), Energy(50), Health(100)
{
// do other constructor stuff here
}
};
In another approach of idea, if you are planning on doing some mecanics inside Player, you may move the declaration inside a real class. You will then be able to scale your project a little more easily. Something like this:
Header
// #include "Item.h"
typedef enum RaceDef {
UNKNOWN,
DEAD,
HUMAN,
ORC,
GOBLIN,
ELF,
LIZARD,
CAT,
VAMPIRE,
WEREWOLF,
SNK
} PlayerRace;
class Player {
public:
Player(unsigned int Charma=0,
unsigned int Hunger=10,
unsigned int Energy=50,
unsigned int Health=100,
PlayerRace Race=HUMAN);
void attack(Player);
void slap(Player);
//void equipItem(Item);
void exercise(unsigned int duration);
void die();
void etc();
private:
unsigned int m_Charma;
unsigned int m_Hunger;
unsigned int m_Energy;
unsigned int m_Health;
unsigned PlayerRace m_Race;
Race m_Race;
};
CPP
Player::Player(unsigned int Charma,
unsigned int Hunger,
unsigned int Energy,
unsigned int Health,
PlayerRace Race):
m_Charma(Charma),
m_Hunger(Hunger),
m_Energy(Energy),
m_Health(Health),
m_Race(Race) {
//constructor code goes here
//e.g. if player starts with a random item :
// Item randomItem = ItemUtils.getRandomItem();
// equipItem(randomItem);
}
I hope this helps, and good luck :)

Why does my derived class constructor not work?

I have been working to create an Arduino library to control a touchscreen. My library builds off of pre-existing libraries written to interface with the display & touch controllers.
Here is the constructor that I have been working on:
Display.cpp
Display::Display(int displayCSPin, int displayDCPin, int touchCSPin,
int newBacklightPin, int newRotation, int newBrightness)
: Adafruit_HX8357(displayCSPin, displayDCPin, -1),
Adafruit_STMPE610(touchCSPin)
{
//Initialize display
}
Display.h
#ifndef DISPLAY_H_
#define DISPLAY_H_
#include "arduino.h"
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#include "Adafruit_STMPE610.h"
class Display : public Adafruit_HX8357, Adafruit_STMPE610 {
public:
Display(int displayCSPin, int displayDCPin, int touchCSPin,
int newBacklightPin, int newRotation, int newBrightness);
};
Whenever I try to compile, the compiler ignores the variables in the base class constructors, and tries to call a default constructor with no variables:
error: no matching function for call to 'Adafruit_HX8357::Adafruit_HX8357()'
I have tried my best to solve this problem, but have not had any success.
Any and all help is greatly appreciated!
Here is the raw code:
Display.cpp
#include "Display.h"
Display::Display(int displayCSPin, int displayDCPin, int touchCSPin, int newBacklightPin, int newRotation, int newBrightness) : Adafruit_HX8357(displayCSPin, displayDCPin, -1), Adafruit_STMPE610(touchCSPin)
{
// tft = Adafruit_HX8357(displayCSPin, displayDCPin, -1);
//ts = Adafruit_STMPE610(touchCSPin);
tft.begin(HX8357D);
ts.begin();
tft.setRotation(newRotation);
backlightPin = newBacklightPin;
pinMode(backlightPin, OUTPUT);
rotation = newRotation;
backgroundColor = HX8357_BLACK;
brightness = newBrightness;
}
Display.h
#ifndef DISPLAY_H_
#define DISPLAY_H_
#include "arduino.h"
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#include "Adafruit_STMPE610.h"
class Display : public Adafruit_HX8357, public Adafruit_STMPE610 {
public:
Display(int displayCSPin, int displayDCPin, int touchCSPin, int newBacklightPin, int newRotation, int newBrightness);
void wake();
void sleep();
bool isAwake();
void setBackGroundColor(int newColor);
int getBackgroundColor();
void setBrightness(int newBrightness);
int getBrightness();
void setRotation(int newRotation);
int getRotation();
bool isTouched();
Adafruit_HX8357 tft;
Adafruit_STMPE610 ts;
int backgroundColor;
private:
int brightness;
int rotation;
int backlightPin;
bool awake;
bool touched;
TS_Point p;
};
Look at the actual constructors available for Adafruit_HX8357 and Adafruit_STMPE610:
Adafruit_HX8357(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK,
int8_t _RST, int8_t _MISO);
Adafruit_HX8357(int8_t _CS, int8_t _DC, int8_t _RST = -1);
Adafruit_STMPE610(uint8_t cspin, uint8_t mosipin, uint8_t misopin, uint8_t clkpin);
Adafruit_STMPE610(uint8_t cs);
Adafruit_STMPE610(void);
Your Display constructor is trying to call the second constructor of each class. However, Display is passing int values where int8_t and uint8_t values are expected. It sounds like maybe your compiler is not performing implicit conversions from int to (u)int8_t, so try using explicit conversions to force it:
Display::Display(int displayCSPin, int displayDCPin, int touchCSPin, int newBacklightPin, int newRotation, int newBrightness)
: Adafruit_HX8357((int8_t)displayCSPin, (int8_t)displayDCPin, -1),
Adafruit_STMPE610((uint8_t)touchCSPin)
{
//Initialize display
}
Otherwise, change you Display constructor parameters to use int8_t and uint8_t instead of int:
Display::Display(int8_t displayCSPin, int8_t displayDCPin, uint8_t touchCSPin, int newBacklightPin, int newRotation, int newBrightness)
: Adafruit_HX8357(displayCSPin, displayDCPin, -1),
Adafruit_STMPE610(touchCSPin)
{
//Initialize display
}

How do I properly handle #includes in multi-file programming

First post so take it easy on me :). I don't think I really need to put up any actual code for this, but let me know if I'm wrong. This is for a homework assignment in my college programming class. I am confused as to how to properly use my #include statements. Here is my file structure:
Header Files-->
header.h (Main header file, contains #include for various libraries, declares namespace, and provides my name and class info)
room.h (Blueprint for the room class)
ship.h (Blueprint for the ship class)
Source Files-->
main.cpp (Main Program)
functions.cpp (Functions for the main program)
room.cpp (Functions in the Room class)
ship.cpp (Functions in the Ship class)
Basically, my first instinct was to " #include "header.h" " in room.h, ship.h, main.cpp, and functions.cpp. Then " #include "ship.h" in ship.cpp, and " #include room.h " in room.cpp. However I began getting errors up the wazoo. I was having a similar problem during class but I had my teacher there to sort it out and I'm not exactly sure how we did it, and I also know that tons of errors usually indicates an include error.
Its annoying because I had it working somehow before I added the functions.cpp, but I really want to keep main.cpp pretty clean, so I would rather have functions in a separate file.
What is the best pattern for includes in a situation like this?
EDIT: I'll post my 3 header files
header.h
/*
Author: *********
Class : **********
Assignment : Programming Assignment 2
Description :
This program will construct a ship for the user. It accepts input from a file
containing information on various rooms. It will then check the rooms
validity and add it to the ship if it's valid. Once all of the rooms have been added,
the program will determine if the entire ship is valid and let the user know.
Certification of Authenticity :
I certify that this is entirely my own work, except where I have given
fully - documented references to the work of others.I understand the
definition and consequences of plagiarism and acknowledge that the assessor
of this assignment may, for the purpose of assessing this assignment :
-Reproduce this assignment and provide a copy to another member of
academic staff; and / or
- Communicate a copy of this assignment to a plagiarism checking
service(which may then retain a copy of this assignment on its
database for the purpose of future plagiarism checking)
*/
#ifndef header_h
#define header_h
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
#endif
room.h
#ifndef room_h
#define room_h
#include "header.h"
enum RoomType
{
UNKNOWN = -1,
BAY,
LATRINE,
CABIN,
BRIDGE,
NUM_ROOM_TYPES
};
const string ROOM_STRINGS[NUM_ROOM_TYPES] = { "Bay",
"Latrine",
"Cabin",
"Bridge"
};
class Room
{
public:
//default constructor
Room();
//constructor
Room( RoomType type, int width, int breadth, int height );
//destructor
~Room(){};
//accessors
inline RoomType getType() const { return mType; };
inline int getHeight() const { return mHeight; };
inline int getWidth() const { return mWidth; };
inline int getBreadth() const { return mBreadth; };
inline int getVolume() const { return getWidth() * getBreadth() * getHeight(); }; //currently unused
inline string getRoomName(){ return ROOM_STRINGS[mType]; };
string getDescription();
//mutators
void setType(RoomType type) {mType = type; };
void setHeight(int height) {mHeight = height; };
void setWidth(int width) {mWidth = width; };
void setBreadth(int breadth) {mBreadth = breadth; };
private:
//type of room
RoomType mType;
//floor dimensions - in feet
int mWidth;
int mBreadth;
//ceiling height - in feet
int mHeight;
};
#endif
ship.h
#ifndef ship_h
#define ship_h
#include "header.h"
const int MAX_BAY = 4;
const int MAX_LATRINE = 15;
const int MAX_BRIDGE = 1;
const int MAX_CABIN = 25;
const int MIN_BAY = 1;
const int MIN_LATRINE = 1;
const int MIN_BRIDGE = 1;
const int MIN_CABIN = 0;
const int MIN_ROOM_HEIGHT = 7;
const int MIN_ROOM_AREA = 20;
class Ship{
public:
Ship();
bool addRoom(const Room& theRoom);
string getDescription();
//Accessors
int getNumBays(){ return bayTotal; };
int getNumLatrines(){ return latrineTotal; };
int getNumBridges(){ return bridgeTotal; };
int getNumCabins(){ return cabinTotal; };
int getTotalSquareFootage(){ return totalSquareFootage; };
private:
Room Bay[MAX_BAY];
Room Latrine[MAX_LATRINE];
Room Bridge[MAX_BRIDGE];
Room Cabin[MAX_CABIN];
int bayTotal;
int latrineTotal;
int bridgeTotal;
int cabinTotal;
int totalSquareFootage;
bool isShipValid();
void addSquareFootage(float);
};
#endif
What kind of errors? Your issue might be including the same header more than once.
Try adding this to each header:
#ifndef ROOM_H
#define ROOM_H
... code ...
#endif
To be clear the 'ROOM_H' above needs to be unique to each header.
If you use #include "room.h" in different cpp files then you probably get a linker error because this below here is not a type declaration.
const string ROOM_STRINGS[NUM_ROOM_TYPES] = { "Bay",
"Latrine",
"Cabin",
"Bridge"
};
You are creating and allocating a variable with name ROOM_STRINGS. By declaring it in different cpp files you will have multiple copies of the same global variable which is an error. You could replace it with
static const string ROOM_STRINGS[NUM_ROOM_TYPES] = { "Bay",
"Latrine",
"Cabin",
"Bridge"
};
You will still have multiple copies but each cpp file will have its own private copy. A better solution is to move this declaration into the room cpp file together with the code of the getRoomName.
Or you could declare ROOM_STRINGS as extern in the header and then you still need to add the variable allocation in a cpp file.

C++ object orientated issue

I am trying to learn C++ OOP and I made the follwing code:
main.cpp
#include <iostream>
#include <string>
#include "monster.h"
using namespace std;
int main(int argc, char** argv) {
Monster monster("Wizard",150,50);
Monster monster2("Gorgoyle",450,15);
cout << monster2.getHealth() << endl;
monster.attack(monster2);
cout << monster2.getHealth() << endl;
}
monster.h
#ifndef MONSTER_H
#define MONSTER_H
#include <iostream>
#include <string>
using namespace std;
class Monster
{
public:
Monster(string name_, int health_, int damage_);
~Monster();
int attack(Monster opponet);
int getHealth();
string name;
int damage;
int health = 0;
int getDamage();
void setHealth(int health_);
void setDamage(int damage_);
void setName(string name);
void doDamageToOpponent(Monster opponent);
string getName();
};
#endif
monster.cpp
#include "monster.h"
Monster::Monster(string name_, int health_, int damage_) {
health = health_;
setDamage(damage_);
setName(name_);
}
Monster::~Monster() { }
int Monster::attack(Monster opponent) {
doDamageToOpponent(opponent);
}
void Monster::doDamageToOpponent(Monster opponent) {
int newHealth = opponent.getHealth() - this->getDamage();
opponent.setHealth(newHealth);
}
int Monster::getHealth() {
return health;
}
int Monster::getDamage() {
return damage;
}
void Monster::setHealth(int health_) {
health = health_;
}
void Monster::setDamage(int damage_) {
this->damage = damage_;
}
void Monster::setName(string name_) {
this->name = name_;
}
string Monster::getName() {
return name;
}
Now my problem is that, when I run this code I expect to have monster2 object to have 400 health left, but it is still 450 :S
What must be done here in order to to so? I noticed that it can be 400 in doDamageToOppoenet but when it leaves that block, then it is still 450. Please help me! Thanks.
You're passing objects by value:
void Monster::doDamageToOpponent(Monster opponent) <- This should be by reference
int Monster::attack(Monster opponent) <- idem
that means: you're creating a new copy of the Monster object you meant to deal damage to in the functions you're calling, and then actually dealing that copy damage but leaving the original old object with the value untouched.
Signatures as follows would work instead:
void Monster::doDamageToOpponent(Monster& opponent)
int Monster::attack(Monster& opponent)
If you want to learn more about this, something to read on: Passing stuff by reference and Passing stuff by value
The reason is that functions attack and doDamageToOpponent are taking copies of arguments, because you pass them by value. What happenes then is you change the copies of passed Monsters inside functions. After functions return, these copies die (as they are local to functions) and nothing happens to original, interested parties.
Try instead pass the argument by reference. Reference works as if it was the original variable. Consider:
int a = 0;
int &refa = a; /* refa acts as real "a", it refers to the same object "a" */
int b = a; /* this is your case */
b = 6; /* b will be changed, but "a" not */
refa = 6; /* a is changed, really "a", refa is just different name for "a" */
Try:
int Monster::attack( Monster &opponent){
doDamageToOpponent( opponent);
}
void Monster::doDamageToOpponent( Monster &opponent){
int newHealth = opponent.getHealth() - this->getDamage();
opponent.setHealth( newHealth);
}
You are passing the opponent by value, i.e., the function:
int Monster::attack(Monster opponent);
will actually receive a copy of the opponent and modify that copy. Every time you have a function that modifies some object you need to pass the object to be modified by reference or pass a pointer to it, e.g.,
int Monster::attack(Monster& opponent);
or
int Monster::attack(Monster* opponent);
I recommend using const T& for input parameters and T* for output parameters, so in this case, the latter form. The reason why I recommend the latter for output parameters is because it makes it more explicit to the caller:
monster.attack(&monster2); // passing a pointer: monster2 will be modified.