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.
Related
Just to make everyone aware. I have to use char array for strings, this is homework and has to be done that way. Also the classes are made on purporse.
I'm supposed to read a fish' name via my Fish class, which is a subclass of Animal class. If the input length is more than 0, then I'll run the constructor with the char array parameter and update the "fishname" inside Fish class. If not, I'll run the constructor without parameter (Fish() constructor).
My questions:
Right now it gives me the option to write in an input, I do that - it crashes. It is the Fish object causing it, but don't know why. How come?
How would I transport the data that I'll get into "fishname" in the Fish data, over to "name" in the Animal class?
So this is what I have made so far, but it only crashes after input.
#include
using namespace std;
#include <iostream>
using namespace std;
class Animal {
private:
char* name;
public:
Animal() { strcpy(name, ""); } // Constructors that set name to nothing
void writeName() { cout << name; } // Function to read an animal's name
};
class Fish : public Animal {
private:
char* fishname;
public:
Fish() {}
Fish(const char* name) { strcpy(fishname, name); }
};
int main() {
char fishname[20];
cout << "Read fish's name: "; cin.ignore();
cin.getline(fishname, 20);
if(strlen(fishname) > 0) Fish f1(fishname);
else Fish f1;
return 0;
}
About the best you can do, short of implementing a lot of the functionality of std::string is to use a fixed size char array. This is not generally a good practice. I would not usually do this, but I will take pity.
#include <iostream>
#include <cassert>
using namespace std; // NEVER write this in a header file. Just saying.
class Animal {
public:
static const int max_name = 128;
Animal() {
name[0] = 0;
}
void writeName() { cout << name; } // Function to read an animal's name
private:
char name[max_name];
};
class Fish : public Animal {
private:
char fishname[Animal::max_name];
public:
Fish() {
fishname[0] = 0;
}
Fish(const char* name) {
assert(strlen(name) < Animal::max_name);
strcpy(fishname, name);
}
};
my program basically depends on setters to initialize the data in my object instances but I want to remove them and have constructors in place of the setters, Is there a way I can do this or can anybody provide me a reference?
Instantiate object
//Using SDL and standard IO
#include <SDL.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <iomanip>
#include <archer.hpp>
#include <ctime>
#include <ArmouredArcher.hpp>
#include <RNGI.hpp>
using namespace std; //Declaring use of namespace std
void instantiateMuskateer();
int main(int argc, char* argv[])
{
//init muskateer object
instantiateMuskateer();
system("pause");
return 0;
}
Instantiation, Activity and destruction
void instantiateMuskateer()
{
Archer* Muskateer = new Archer();
Muskateer->setName("Brett");
delete Muskateer;
}
.hpp file
#ifndef _Archer_
#define _Archer_
#include <string>
class Archer
{
public:
inline Archer() :
name(""),
healthpoints(0),
baseDamage(0),
range(0)
{ ; } //All Member varials are in a known state
inline Archer(std::string name, int healthpoints, int baseDamage, int range) :
name(name),
healthpoints(healthpoints),
baseDamage(baseDamage),
range(range) //All member variables are in a known state
{
;
}
inline ~Archer() { ; } // empty destructor
inline std::string getName() { return name; }
inline void setName(std::string name) { this->name = name; }
inline int getHealthPoints() { return healthpoints; }
inline void setHealthPoints(int healthpoints) { this->healthpoints = healthpoints; }
inline int getBaseDamage() { return baseDamage; }
inline void setBaseDamage(int baseDamage) { this->baseDamage = baseDamage; }
inline int getRange() { return range; }
inline void setRange(int range) { this->range = range; }
/*std::string getName(); //getter for name
void setName(std::string name); //Set the name
int getHealthPoints();
void setHealthPoints(int healthpoints);
int getBaseDamage();
void setBaseDamage(int baseDamage);
int getRange();
void setRange(int range); */
protected:
private:
// copy constructor
Archer(const Archer& other) = delete;
// overload assignment operator
Archer& operator=(const Archer& other) = delete;
std::string name;
int healthpoints;
int baseDamage;
int range;
};
#endif
In your example, it is really simple, you just have to take the parameters you need in your constructor:
Archer(std::string n) :
name(n),
healthpoints(0),
baseDamage(0),
range(0)
{} //All Member varials are in a known state
And then you can simply do that:
void instantiateMuskateer()
{
Archer* Muskateer = new Archer("Brett");
delete Muskateer;
}
A few comments not related, but to improve your code. Writing inline is useless when you declare and implement your functions inside your class, the inline is implied. Also, if your destructor does nothing, you should not define it or use = default, that way you can enable some optimizations from the compiler.
Also, in your previous function i see no need to allocate the object on the heap, it is again a loss of performance and a source of error (such as forgetting to delete the object), allocate it on the stack:
void instantiateMuskateer()
{
Archer Muskateer("Brett");
// do your things
}
Or use a unique_ptr.
I am learning classes and OOP, so I was doing some practice programs, when I came across the weirdest bug ever while programming.
So, I have the following files, beginning by my class "pessoa", located in pessoa.h:
#pragma once
#include <string>
#include <iostream>
using namespace std;
class pessoa {
public:
//constructor (nome do aluno, data de nascimento)
pessoa(string newname="asffaf", unsigned int newdate=1996): name(newname), DataN(newdate){};
void SetName(string a); //set name
void SetBornDate(unsigned int ); //nascimento
string GetName(); //get name
unsigned int GetBornDate();
virtual void Print(){}; // print
private:
string name; //nome
unsigned int DataN; //data de nascimento
};
Whose functions are defined in pessoa.cpp
#include "pessoa.h"
string pessoa::GetName ()
{
return name;
}
void pessoa::SetName(string a)
{
name = a;
}
unsigned int pessoa::GetBornDate()
{
return DataN;
}
void pessoa::SetBornDate(unsigned int n)
{
DataN=n;
}
A function, DoArray, declared in DoArray.h, and defined in the file DoArray.cpp:
pessoa** DoArray(int n)
{
pessoa* p= new pessoa[n];
pessoa** pointer= &p;
return pointer;
}
And the main file:
#include <string>
#include <iostream>
#include "pessoa.h"
#include "DoArray.h"
#include <cstdio>
using namespace std;
int main()
{
//pessoa P[10];
//cout << P[5].GetBornDate();
pessoa** a=DoArray(5);
cerr << endl << a[0][3].GetBornDate() << endl;
cerr << endl << a[0][3].GetName() << endl;
return 0;
}
The weird find is, if I comment one of the methods above, "GetBornDate" or GetName, and run, the non-commented method will run fine and as supposed. However, if both are not commented, then the first will run and the program will crash before the 2nd method.
Sorry for the long post.
Let's look into this function:
int *get()
{
int i = 0;
return &i;
}
what is the problem with it? It is returning pointer to a local variable, which does not exist anymore when function get() terminates ie it returns dangling pointer. Now your code:
pessoa** DoArray(int n)
{
pessoa* p= new pessoa[n];
return &p;
}
do you see the problem?
To clarify even more:
typedef pessoa * pessoa_ptr;
pessoa_ptr* DoArray(int n)
{
pessoa_ptr p= whatever;
return &p;
}
you need to understand that whatever you assign to p does not change lifetime of p itself. Pointer is the same variable as others.
I have to set up an object and, after an user chose, i have to change some param into the object but not every each.
example:
{
class Champ
{
private:
int hp;
std::string class;
public:
Champ();
Champ(std::string chose);
};
Champ::Champ() {hp=10; class="";}
Champ::Champ(std::string chose) {class = chose;}
main()
{
Champ Test;
std::string chose;
getline(cin,chose);
Test(chose);
return 0;
}
this code give me an error.
i need hp equal for all "Champ" created but class can be changed.
The hp can't be "const" because this value may undergo changes...
how can i do this? :/
The comments in the code below should explain what is going on well enough...
#include <iostream>
#include <string>
class Champ {
int hp;
std::string job;
public:
Champ():
hp(10) { } // don't need to explicitly initialize `job` because the default constructor for string does what we want.
explicit Champ(const std::string& choose):
hp(10),
job(choose) { }
};
int main(int argc, const char * argv[]) {
using namespace std;
// this is most like how you had it with the compile error fixed.
{
Champ test; // this creates a Champ object using the default constructor
string choose;
getline(cin, choose);
test = Champ(choose); // this creates a new Champ object and assigns it to test... Throwing away the one that was created earlier.
}
// this is, imho, a better way to do it:
{
string choose;
getline(cin, choose);
auto test = Champ(choose); // declare the variable as late as possible, and after you have all the data for its construction. That way, you only make one of them.
}
return 0;
}
I'm having trouble declaring and initializing a char array. It always displays random characters. I created a smaller bit of code to show what I'm trying in my larger program:
class test
{
private:
char name[40];
int x;
public:
test();
void display()
{
std::cout<<name<<std::endl;
std::cin>>x;
}
};
test::test()
{
char name [] = "Standard";
}
int main()
{ test *test1 = new test;
test1->display();
}
And sorry if my formatting is bad, I can barely figure out this website let alone how to fix my code :(
If there are no particular reasons to not use std::string, do use std::string.
But if you really need to initialize that character array member, then:
#include <assert.h>
#include <iostream>
#include <string.h>
using namespace std;
class test
{
private:
char name[40];
int x;
public:
test();
void display() const
{
std::cout<<name<<std::endl;
}
};
test::test()
{
static char const nameData[] = "Standard";
assert( strlen( nameData ) < sizeof( name ) );
strcpy( name, nameData );
}
int main()
{
test().display();
}
Your constructor is not setting the member variable name, it's declaring a local variable. Once the local variable goes out of scope at the end of the constructor, it disappears. Meanwhile the member variable still isn't initialized and is filled with random garbage.
If you're going to use old-fashioned character arrays you'll also need to use an old-fashioned function like strcpy to copy into the member variable. If all you want to do is set it to an empty string you can initialize it with name[0] = 0.
Since you are using C++, I suggest using strings instead of char arrays. Otherwise you'd need to employ strcpy (or friends).
Also, you forgot to delete the test1 instance.
#include <iostream>
#include <string>
class test
{
private:
std::string name;
int x;
public:
test();
void display()
{
std::cout<<name<<std::endl;
}
};
test::test()
{
name = "Standard";
}
int main()
{
test test1;
test1.display();
std::cin>>x;
}
Considering you tagged the question as C++, you should use std::string:
#include <string>
class test
{
private:
std::string name;
int x;
public:
test();
void display()
{
std::cout<<name<<std::endl;
std::cin>>x;
}
};
test::test() : name("Standard")
{
}
c++11 actually provides two ways of doing this. You can default the member on it's declaration line or you can use the constructor initialization list.
Example of declaration line initialization:
class test1 {
char name[40] = "Standard";
public:
void display() { cout << name << endl; }
};
Example of constructor initialization:
class test2 {
char name[40];
public:
test2() : name("Standard") {};
void display() { cout << name << endl; }
};
You can see a live example of both of these here: http://ideone.com/zC8We9
My personal preference is to use the declaration line initialization because:
Where no other variables must be constructed this allows the generated default constructor to be used
Where multiple constructors are required this allows the variable to be initialized in only one place rather than in all the constructor initialization lists
Having said all this, using a char[] may be considered damaging as the generated default assignment operator, and copy/move constructors won't work. This can be solved by:
Making the member const
Using a char* (this won't work if the member will hold anything but a literal string)
In the general case std::string should be preferred