I'm learning about polymorphism and this is a small game. I have a representative class Character here, I want to program so that from a Character people can choose Warrior or Archer to continue the game.
#pragma once
#include <iostream>
using namespace std;
#include <string>
class Warrior;
class Archer;
class Character {
public:
Character(void);
~Character(void);
Character* creatCharacter(int choice, string CharacterName) {
if (choice == 1)
return (Character*)new Warrior(CharacterName);
if (choice == 2)
return (Character*)new Archer(CharacterName);
return NULL;
}
virtual void Skill_Cast() {};
};
class Warrior :public Character {
private:
string name;
public:
Warrior(void);
~Warrior(void);
Warrior(string CharacterName) {
name = CharacterName;
}
void Skill_Cast() {
cout << "Punch!" << endl;
}
};
class Archer : public Character
{
private:
string name;
public:
Archer(void);
~Archer(void);
Archer(string CharacterName) {
name = CharacterName;
}
void Skill_Cast() {
cout << "Shoot!" << endl;
}
};
In the main function:
int main() {
cout <<"Enter character's name: ";
string name;
getline(cin, name, '\n');
cout <<"Enter your character class by number (1),(2),(3): ";
int choice;
cin >> choice;
Character* YourChar;
YourChar = YourChar->creatCharacter(choice, name);
YourChar->Skill_Cast();
}
And this is ERRORS:
Error 1 error C2512: 'Warrior' : class has no constructors
Error 2 error C2514: 'Archer' : class has no constructors
Can you explain me the errors and help me fix that, by the way, Is this a kind of "Abstract Factory Design Pattern" ? Thanks so much.
(sorry for my bad English)
Rearrange your files into header/source files. It will make your code much cleaner and easier to read and it will also solve your problem.
// Archer.h
#pragma once
#include "Character.h"
class Archer : public Character
{
public:
Archer(void);
Archer(std::string CharacterName);
~Archer(void);
void Skill_Cast();
private:
std::string name;
};
// Character.h
#pragma once
#include <string>
class Character
{
public:
Character(void);
~Character(void);
Character* creatCharacter(int choice, std::string CharacterName);
virtual void Skill_Cast() {};
};
// Character.cpp
#include "Warrior.h"
#include "Archer.h"
Character* Character::creatCharacter(int choice, std::string CharacterName)
{
if (choice == 1)
return (Character*)new Warrior(CharacterName);
if (choice == 2)
return (Character*)new Archer(CharacterName);
return NULL;
}
I haven't done all the work for you but this should point you in the right direction.
If you are set on using Abstract Factory (which is rarely useful), do it right. You should not have your createChracater (defined within Character class - base class should not know anything about it's descendants). Instead, you should have a separate file, with separate function, like following:
CharacterFactory.h
#include <character.h>
#include <memory>
std::unique_ptr<Character> make_character(int type, std::string name);
CharacterFactory.cpp
#include <warrior.h>
#include <archer.h>
#include <stdexcept>
std::unique_ptr<Character> make_character(int type, std::string name) {
if (type == 1)
return std::unique_ptr<Character>(new Archer(name));
if (type == 2)
return std::unique_ptr<Character>(new Warrior(name));
throw std::runtime_error("Unknown character type requested!");
}
In this snippet ... you need not (and should not) cast the derived instance back to the base class:
Character* creatCharacter(int choice, string CharacterName)
{
if (choice == 1)
return (Character*)new Warrior(CharacterName);
if (choice == 2)
return (Character*)new Archer(CharacterName);
return NULL;
}
For polymorphism to work, your derived classes must inherit from the base. It looks like your base class is "Character", so this code should be more like the following. The idea is that Warrior is-a Character (as Archer is-a Character), so you need not (shall not) cast.
Character* creatCharacter(int choice, string CharacterName)
{
if (choice == 1)
return (new Warrior(CharacterName));
if (choice == 2)
return (new Archer(CharacterName));
return NULL;
}
When used, simply invoke the Character action you wish.
For example, assume Character has the method
virtual void Character::action1(){ ... do stuff }; // body in .cc file
Then, with a warrior instance, you can invoke action1() as in:
Warrior warrior;
warrior.action1(); // because warrior "is-a" 'Character'.
Or, more typically, from a pointer in an array of Character.
std::vector<Character*> actionFigures;
for ( << maybe all figures in vector >> )
actionFigures[i]->action1();
The above invokes Character::action1() only if the derived instance does not replace the method.
If Warrior re-defines action1(), then its version of the action1() method will be invoked.
There is also another somewhat bigger (though very common) mistake you have started down: the point of polymorphism is that the base class does NOT (shall NOT, CAN NOT) know what might be derived from it. Derived classes might be added many code versions later, with possibly NO changes to the base class. (i.e. code re-use)
This function is 'broken' in the sense that for every new derived Character you will have to modify this function, re-test everything, etc. (not code re-use)
This is just additional guidance, not trying to write the code for you.
Good luck.
Related
I've got a base class Container with a derived class Player_Inventory. There can only be one Player_Inventory so my code throws an exception if for some reason a second one is created.
The problem I'm having is that my code is failing my test as it throws the exception even on what is supposed to be the very first construction of the Player_Inventory class. I've debugged the code and two things are happening which I don't quite understand - the number attribute is not tracked by the debugger (at least not in the GUI on VSC), and it seems that right after hitting the first REQUIRE statement, the constructor is called again, thus triggering the exception.
Can anyone help?
After rewriting my constructor method, I'm still getting a similar error.
My revised code is as follows:
containers.h
#include<iostream>
#include<string>
#include<vector>
class Item { // Placeholder class for items
public:
std::string name;
Item(std::string n) : name{n} {};
};
class Container {
protected:
std::string name;
std::string description;
std::vector<Item> contents;
public:
Container(std::string, std::string);
std::string get_name() {return name;}
std::string get_description() {return description;}
std::vector<Item> get_contents() {return contents;}
};
containers.cpp (there are more methods defined in this file which aren't used here)
#include<iostream>
#include<string>
#include "containers.h"
Container::Container(std::string n, std::string desc) : name{n}, description{desc} {};
player_inventory.h
#include "containers.h"
class Player_Inventory : public Container {
public:
static int number;
Player_Inventory(std::string, std::string);
};
player_inventory.cpp
#include<iostream>
#include<stdexcept>
#include "player_inventory.h"
Player_Inventory::Player_Inventory(std::string n, std::string desc): Container(n, desc) {
number += 1;
if (number > 1){
throw std::invalid_argument("You can only have one inventory!");
}
};
int Player_Inventory::number = 0;
test_file.cpp
#include "../lib/Catch2/catch.hpp"
#include "player_inventory.h"
#include<iostream>
#include<string>
#include<vector>
SCENARIO("A player can have an inventory.") {
WHEN("A player inventory is created.") {
Player_Inventory myInventory("My Inventory", "Inventory for the player");
THEN("The created inventory has the correct attribute values.") {
REQUIRE(myInventory.get_name() == "My Inventory");
REQUIRE(myInventory.get_description() == "Inventory for the player");
REQUIRE(myInventory.get_contents().empty());
} // The code works fine when only up to here is included
AND_THEN("Only one player inventory can exist.") { // as soon as this line is included it tries to create another player_inventory object, causing the fail
REQUIRE_THROWS((Player_Inventory myOtherInventory("Second Inventory", "Testing for another one"))); // These two lines were not included but I've included them here as this is the test I wanted to run
REQUIRE(myInventory.get_number() == 1);
}
}
}
Not sure if related, but that's how you should call the Base constructor:
Player_Inventory(std::string n, std::string desc) : Container(n, desc) {
}
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);
}
};
I am new to C++ as I made the switch from Java/C#. Can somebody explain why my code doesn't work like I think it should. I have a simple hierarchy of Animal class which is then inherited by Dog and Cat. The only difference between the classes is their virtual method toString() /which obviously returns a string based on which of the classes it is called on/. Okay so I am inputting information and creating the classes with cin and pushing them into a vector of Animals. However when I tried to call their toString() I got the result from the base toString() and not the overriden one. Here is the code at this point:
#include <iostream>
#include <vector>
#include "animal.h"
#include "cat.h"
#include "dog.h"
using namespace std;
int main()
{
vector<Animal> animals;
string type;
string name;
int age;
int weight;
while(cin >> type){
cin >> name >> age >> weight;
if(type == "dog"){
animals.push_back(Dog(name, age, weight);
}
else {
animals.push_back(Cat(name, age, weight);
}
}
for(vector<Animal>::iterator iter = animals.begin(); iter != animals.end();
iter++){
cout << iter -> toString() << endl;
}
return 0;
}
But then after I did some googling I found a suggestion that I should use pointers because of something called object slicing. So then my code turned into this:
#include <iostream>
#include <vector>
#include "animal.h"
#include "cat.h"
#include "dog.h"
using namespace std;
int main()
{
vector<Animal*> animals;
string type;
string name;
int age;
int weight;
while(cin >> type){
cin >> name >> age >> weight;
if(type == "dog"){
Dog tempDog(name, age, weight);
animals.push_back(&tempDog);
}
else {
Cat tempCat(name, age, weight);
animals.push_back(&tempCat);
}
}
for(vector<Animal*>::iterator iter = animals.begin(); iter != animals.end();
iter++){
cout << iter -> toString() << endl;
}
return 0;
}
And now I am getting a compiler error suggesting I should use '->';
Also a side question while I am here I would like to ask. Is there a way of overriding a virtual method from the .cpp file and not the header file where the class is defined. I am recently getting into the oop in c++ and to my idea is that in the header file I just define prototypes of the members of the class and I do the implementation in a different .cpp file.
cout << iter -> toString() << endl;
Is attempting to call a member function of the type of *iter. Since *iter is an Animal* it does not have any member functions. What you need to do is get the value of the iterator and then call its member function with -> like
cout << (*iter)->toString() << endl;
Also note that if you have access to C++11 or higher you can use a ranged based for loop like
for (auto& e : animals)
cout << e->toString() << "\n";
I also changed to "\n" instead of endl as typically you do not need the call to flush so you should only do that when you know you need to.
You also have undefined behavior in your code.
if(type == "dog"){
Dog tempDog(name, age, weight);
animals.push_back(&tempDog);
}
Is going to add a pointer to a automatic object into the vector. When you leave the if block that automatic object get automatically destroyed. After it is destroyed you now have a pointer to an object that no longer exist. The quick fix is to use new to dynamically allocate the object like
if(type == "dog"){
Dog* tempDog = new Dog(name, age, weight);
animals.push_back(tempDog);
}
Now the pointer in the vector will still be valid. Unfortunately now you need to remember to delete all of those pointers when you are done with them. Instead of having to do manual memory management you can use a smart pointer like a std::unique_ptr or std::shared_ptr which will manage the memory for you.
This question already has answers here:
Updating vector of class objects using push_back in various functions
(2 answers)
Closed 7 years ago.
I am attempting to make a text adventure sort of game, and I would like to avoid a bunch of conditionals, so I am trying to learn about the classes stuff and all that. I have created several classes, but the only ones that pertain to this problem are the Options class and the Items class. My problem is that I am trying to push_back() a object into a vector of the type of that object's class and it apparently doesn't happen yet runs until the vector is attempted to be accessed. This line is in main.cpp. I have researched on this, but I have not been able to find a direct answer, probably because I'm not experienced enough to not know the answer in the first place.
The program is separated into 3 files, main.cpp, class.h, and dec.cpp.
dec.cpp declares class objects and defines their attributes and all that.
main.cpp:
#include <iostream>
#include "class.h"
using namespace std;
#include <vector>
void Option::setinvent(string a, vector<Item> Inventory, Item d)
{
if (a == op1)
{
Inventory.push_back(d);
}
else {
cout << "blank";
}
return;
}
int main()
{
vector<Item> Inventory;
#include "dec.cpp"
Option hi;
hi.op1 = "K";
hi.op2 = "C";
hi.op3 = "L";
hi.mes1 = "Knife";
hi.mes2 = "Clock";
hi.mes3 = "Leopard!!";
string input1;
while (input1 != "quit")
{
cout << "Enter 'quit' at anytime to exit.";
cout << "You are in a world. It is weird. You see that there is a bed in the room you're in." << endl;
cout << "There is a [K]nife, [C]lock, and [L]eopard on the bed. Which will you take?" << endl;
cout << "What will you take: ";
cin >> input1;
hi.setinvent(input1, Inventory, Knife);
cout << Inventory[0].name;
cout << "test";
}
}
dec.cpp just declares the Item "Knife" and its attributes, I've tried pushing directly and it works, and the name displays.
class.h
#ifndef INVENTORY_H
#define INVENTORY_H
#include <vector>
class Item
{
public:
double damage;
double siz;
double speed;
std::string name;
};
class Player
{
public:
std::string name;
double health;
double damage;
double defense;
double mana;
};
class Monster
{
public:
double health;
double speed;
double damage;
std::string name;
};
class Room
{
public:
int x;
int y;
std::string item;
std::string type;
};
class Option
{
public:
std::string op1;
std::string op2;
std::string op3;
std::string mes1;
std::string mes2;
std::string mes3;
void setinvent(std::string a, std::vector<Item> c, Item d);
};
#endif
Any help would be greatly appreciated! I realize that the whole structure may need to be changed, but I think that this answer will help even if that may be the case.
My problem is that I am trying to push_back() a object into a vector of the type of that object's class and it apparently doesn't happen yet runs until the vector is attempted to be accessed.
it happen but only inside your setinvent method:
void Option::setinvent(string a, vector<Item> Inventory, Item d)
^^^^^^^^^^^^ - passed by value
Inventory is passed by value which means it is a local vector variable in setinvent function. If you want to modify vector from main function, make it a reference:
void Option::setinvent(string a, vector<Item>& Inventory, Item d)
^^^^^^^^^^^^ - passed by reference, modifies vector from main
now Inventory is local reference variable. Also dont forget to change setinvent declaration in header file.
We have a parent class called Student. We have a child class: StudentCS.
Student.h:
#include <iostream.h>
#include<string.h>
#include<vector.h>
#include "Course.h"
class Course;
class Student {
public:
Student();
Student(int id, std::string dep, std::string image,int elective);
virtual ~Student();
virtual void Study(Course &c) const; // this is the function we have a problem with
void setFailed(bool f);
[...]
};
Student.cpp:
#include "Student.h"
[...]
void Student::Study(Course &c) const {
}
And we have StudentCS.h:
#include "Student.h"
class StudentCS : public Student {
public:
StudentCS();
virtual ~StudentCS();
StudentCS (int id, std::string dep, std::string image,int elective);
void Study(Course &c) const;
void Print();
};
And StudentCS.cpp:
void StudentCS:: Study (Course &c) const{
//25% to not handle the pressure!
int r = rand()% 100 + 1;
cout << r << endl;
if (r<25) {
cout << student_id << " quits course " << c.getName() << endl;
}
}
We create student in the main:
Student *s;
vector <Student> uniStudent;
[...]
if(dep == "CS")
s = new StudentCS(student_id,dep,img,elective_cs);
else
s = new StudentPG(student_id,dep,img,elective_pg);
uniStudent.push_back(*s);
Then we call to study, but we get the parent study, and not the child!
Please help!
The code compiles but when run and called on the uniStudent.Study() it uses the parent and not the child
EDIT: after your edit the problem is clear.
The problem is that you are storing base concrete objects in a STL container. This creates a problem called object slicing.
When you add a student to a vector<Student>, since the allocator of the vector is built on the Student class, every additional information on derived classes is just discarded. Once you insert the elements in the vector they become of base type.
To solve your problem you should use a vector<Student*> and store directly references to students in it. So the allocator is just related to the pointer and doesn't slice your objects.
vector<Student*> uniStudent;
...
uniStudent.push_back(s);
uniStudent[0]->study();
Mind that you may want to use a smart pointer to manage everything in a more robust way.