How should I call a player spell in a text RPG? - c++

I'm trying to create a mechanic that fills a vector with Spell objects, each with its own name, then select the spell with cin input and cast it on a target. What's the best way to do it? This is what I've done, but what if the spell has multiple spell effects?
//Spell.h
class Spell
{
public:
enum e_spellType //enum with all spells
{
FIREBALL = 1,
FROSTBOLT
};
enum e_spellEffect //enum with different effects
{
DAMAGE = 1, //for damaging effect
SLOW
};
Spell(e_spellEffect effect);
void returnSpellEffect(Unit* target);
//getters here
string getSpellName() const { return m_SpellName; }
int getSpellValue() const { return m_SpellValue; }
int getCooldown() const { return m_Cooldown; }
int getManaCost() const { return m_ManaCost; }
protected:
string m_SpellName;
int m_SpellValue;
int m_Cooldown;
int m_ManaCost;
int m_SpellID;
e_spellEffect m_spellEffect;
e_spellType m_spellType;
};
Spell::Spell(e_spellType type)
{
m_spellType = type;
switch (m_spellType)
{
case 1: //Fireball
m_SpellValue = 35;
m_ManaCost = 40;
m_Cooldown = 2;
m_spellEffect = DAMAGE;
case 2: //Frostbolt
m_SpellValue = 30;
m_ManaCost = 40;
m_Cooldown = 2;
m_spellEffect = SLOW;
}
}
void Spell::returnSpellEffect(Unit * target)
{
switch (m_SpellEffect)
{
case DAMAGE:
target->takeDamage(m_SpellValue);
break;
case SLOW:
target->setDamage(0.5); //modifies Unit object's attack dmg to half
break;
default:
break;
}
}
//Game.h
class Game
{
public:
void enemyCombat();
protected:
Player *player;
vector<Enemy*> enemyList;
vector<Spell*> spellList;
};
void Game::enemyCombat()
{
//after you have chosen a target from enemyList (enemyList[target])
spellList.push_back(new Spell(FIREBALL));
spellList.push_back(new Spell(FROSTBOLT));
cout << "Choose a spell to cast:" << endl
<< "1. Fireball" << endl
<< "2. Frostbolt" << endl;
int spellChoice = 0;
cin >> spellChoice;
spellList[spellChoice-1]->returnSpellEffect(enemyList[target]);
}
How do I make this whole thing more abstract to allow a spell to use more than one spell effect?

Consider using polymorphism. If you have a virtual function doSpellEffects, you can implement "usual" logic in the base class, and more specialized logic in other classes for specific spells or spell categories.
class Spell
{
public:
// Copying disabled to avoid slicing.
Spell(const Spell&) = delete;
Spell& operator=(const Spell&) = delete;
virtual ~Spell() = default;
enum e_spellType { /*...*/ };
// TBD whether e_spellEffect belongs in Spell or SimpleSpell.
// Factory function:
static std::unique_ptr<Spell> create(e_spellType spellType);
const std::string& getSpellName() const noexcept { return m_SpellName; }
int getCooldown() const noexcept { return m_Cooldown; }
int getManaCost() const noexcept { return m_ManaCost; }
virtual void doSpellEffects(Unit* target) = 0;
protected:
Spell(e_spellType spellType) :
m_spellType(spellType), m_SpellName(),
m_Cooldown(0), m_ManaCost(0) {}
e_spellType m_spellType;
std::string m_SpellName;
int m_Cooldown;
int m_ManaCost;
};
class SimpleSpell : public Spell
{
public:
SimpleSpell(e_spellType spellType);
void doSpellEffects(Unit* target) override;
int getSpellValue() const { return m_SpellValue; }
protected:
e_spellEffect m_spellEffect;
int m_SpellValue;
};
class WarlocksRay : public Spell
{
public:
WarlocksRay() : Spell(WARLOCKS_RAY, "Warlock's Ray") {}
void doSpellEffects(Unit* target) override;
};
void WarlocksRay::doSpellEffects(Unit* target)
{
// Two effects!
target->takeDamage(5);
target->stun();
}
// The factory function that creates all spells:
std::unique_ptr<Spell> Spell::create(e_spellType spellType) {
switch(spellType) {
case FIREBALL:
case FROSTBOLT:
return std::make_unique<SimpleSpell>(spellType);
case WARLOCKS_RAY:
return std::make_unique<WarlocksRay>();
}
// Invalid spellType: Log an error? Throw an exception? Just return nullptr?
throw std::invalid_argument("Bad spellType in Spell::create");
}
You could use subclassing in other ways, which might or might not be worth it:
Instead of a switch in SimpleSpell::doSpellEffects, create classes for each common effect type, like DamageSpell and SlowSpell.
If the "cooldown" and/or "mana cost" mechanics might not apply to all spells, move these members and related logic out of Spell into a class NormalCastingSpell or something, which would come between Spell and other classes in the heirarchy.
Even go so far as to create a class for each individual spell. In some cases, this could just inherit SimpleSpell or DamageSpell or etc., and the only member it would need to define would be a constructor that correctly sets all data members.

aschepler's answer is probably the most flexible one, in worst case, though, you might end up in implementing every spell on its own. A variation of could be:
a base class Effect
deriving classes DamageEffect, SlowEffect, ...
one single Spell class
The spell class then might look like this:
class Spell
{
std::string name;
std::vector<std::unique_ptr<Effect>> effects;
public:
void cast(Unit& target)
{
for(auto& effect : effects)
effect->applyTo(target);
}
}
When the spell gets casted, you likely would want to show some appropriate visual effect. You could again have polymorphic objects for these and provide one to the spell class as a member (several similar spells could re-use the same animation that way), alternatively you could have an animation for every effect and use the one of the first element in the effects vector.
Side note: You might create every spell just once in some global vector (not getting changed after creation any more, so no re-allocations – best have it const), units being able to cast spells would then just have pointers to those in their own vector.

Related

Encapsulation along with constructors

I want the int Medals which I put private to be unable to have negative values, but I don't know how to implement that encapsulation along with constructors. I made it so that each athlete type inherits the Athlete constructor but I don't know where to call the setMedals function for it to work.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Athlete {
private:
int Medals;
public:
string Name;
void setMedals(int newMedals) {
if (newMedals >= 0)
Medals = newMedals;
}
int getMedals() const{
return Medals;
}
virtual string getDescription() = 0;
Athlete(string _Name, int _Medals) : Name(_Name), Medals(_Medals) {}
};
class Footballer : public Athlete {
public:
string getDescription() {
return "Footballer ";
}
Footballer(string Name, int Medals) : Athlete(Name, Medals) {}
};
class Basketballer : public Athlete {
public:
string getDescription() {
return "Basketballer ";
}
Basketballer(string Name, int Medals) : Athlete(Name, Medals) {}
};
ostream& operator <<(ostream& output, vector<Athlete*> athletes) {
for (int i = 0; i < athletes.size(); i++) {
output << athletes[i]->getDescription() << " " << athletes[i]->Name << ": " << athletes[i]->getMedals() << " Medals" << endl;
}
return output;
}
void printAthletes(vector<Athlete*> athletes) {
sort(athletes.begin(), athletes.end(), [](Athlete* a, Athlete* b) {
return a->getMedals() > b->getMedals(); });
cout << athletes;
}
int main() {
Footballer Andrew("Andrew", 3), Jack("Jack", 4);
Basketballer David("David", 5), Rob("Rob", 1);
vector<Athlete*> Athlete = { &Andrew, &Jack, &David, &Rob };
printAthletes(Athlete);
return 0;
}
I hope you understand my question cause I don't know how else to phrase it.
From the function:
void setMedals(int newMedals) {
if (newMedals >= 0)
Medals = newMedals;
}
It appears you want to set the value of Medals when it is positive and do nothing otherwise. For this to work you will first have to provide a different initializer for Medals. Some value it will take when the value supplied to the constructor is wrong. Note that you can make the member unsigned when anyhow it should store only positive values. Eventually, you can call setMedals in the Athlete constructor. Normally it is preferable to initialize members rather than assignment in the constructor. However, as you want initialization + optional assignment, doing both is ok:
class Athlete {
private:
unsigned Medals = 0; // <- initializer here
public:
string Name;
void setMedals(int newMedals) {
if (newMedals >= 0)
Medals = newMedals;
}
int getMedals() const{
return Medals;
}
virtual string getDescription() = 0;
Athlete(string _Name, int _Medals) : Name(_Name) { // <- no initializer here
setMedals(_Medals); // <- call the setter
}
};
Because the constructor does not provide an initializer for Medals the in-class initializer (= 0) is used. The member is unsigned and can only possibly take positive values, but as you want to check if the value supplied by subclasses or callers of setMedals was negative, the argument must be signed.
tl;dr:
Calling non-virtual function inside a constructor is generally fine, although I'd pay attention to it when dealing with larger objects.
So, sth like this should do:
class Athlete
{
private:
unsigned medals{0};
public:
string name;
void setMedals(int m) //or just use unsigned...
{
if (m >= 0)
medals = m;
}
unsigned getMedals() const{return medals;}
virtual string getDescription() = 0; //should be const maybe?
Athlete(string n, int m) : name(move(n))
{
setMedals(m);
}
};
As for the extended answer:
Firstly, a short disclaimer. I wonder if I should be answering that here, or maybe flag it to the moderator to move the topic topic to software engineering or some other, similar SE site, as the discussion is likely to steer away into a general "architectural" one.
If either moderators, users or the OP him/herself feel like it, please do so.
Having said that, on topic: the first answer, i.e. to use unsigned int is good enough.
However, one may wonder what the whole purpose of getters and setters is, if they are literally a pass-through to access the variable, thus no real encapsulation is there.
For that reason, one may simply consider sth like this (interface is simplified for brevity):
struct Athlete
{
unsigned medals;
};
If some sort of input validation/processing is needed, e.g. medals cannot exceed 10, one can consider using a setter and getter, e.g.
class Athlete
{
public:
explicit Athlete(unsigned m)
: medals {clamp(m, 0, 10)}
//or throw from constructor
//depedns what one wants really
{}
unsigned getMedals() const { return medals; }
void setMedals(unsigned m) { medals = clamp(m, 0, 10); }
//again, you may throw or do anything else
//clamping is just an example
private:
unsigned medals;
};
However, a question about object's responsibility arises here.
Maybe it's not the Athlete that should care about the number of medals (or whatever the value represents), but the Medals variable iself should be distinct type maintaining its own invariance.
Should one decide to chase this approach, it can look like this:
template <typename T, T LO, T HI>
class LimitedInt
{
//all the needed operations
};
struct Athlete
{
using Medals = LimitedInt<unsigned, 0, 10>;
Medals medals;
};
Unfortunately, no easy answers here which one is better or worse, it depends on various factors, let alone code style and frameworks used are one of them, e.g. QT uses getters and setters extensively by convention.
I think you should, in your constructor, default the medals value to 0, so that if it is a negative number it won't be assigned to your property.
Then in your constructor method, you can call your "setMedals" method.
Hope it was helpful.
You can use unsigned value to make sure that the variable will not have negative values.
unsigned int Medals {0};
And the set function would be:
void setMedals(unsigned int newMedals)
{
Medals = newMedals
}

Inheritance and pointers

I have code like this:
class Human
{
protected:
int age;
std::string sex;
public:
virtual void speak() = 0;
};
class Child:public Human
{
public:
void speak(){std::cout << "I am Child\n";}
};
class Man:public Human
{
public:
void speak(){std::cout << "I am Man\n";}
};
class Woman:public Human
{
public:
void speak(){std::cout << "I am Woman\n";}
};
(don't know, std::shared_ptr<Human> maybe?) operator*(std::shared_ptr<Child> &b, int x)
{
b->setAge(b->getAge()+x);
if(b->getAge()>18 && b->getSex()=="Man")
{
return (i want b to become std::shared_ptr<Man>)
}
if(b->getAge()>18 && b->getSex()=="Woman")
{
return (here I want b to become std::shared_ptr<Woman>);
}
return;
}
int main(){
auto x = std::make_shared<Child>;
x*19;
}
I know it seems odd, but it's the simplest case i can think of, without having to write down all code i'm struggling with rn. Could someone explain, what type should overload be and how to change shared_ptr type, knowing they derive from same parent?
Objects cannot change type. A Child object will always be a Child object. What you can do is create a new object with the properties you want and return that:
std::shared_ptr<Human> operator*(std::shared_ptr<Human> b, int x)
{
b->setAge(b->getAge()+x);
if(b->getAge()>18 && b->getSex()=="Man") {
return std::make_shared<Man>(b->getAge());
} else if(b->getAge()>18 && b->getSex()=="Woman") {
return std::make_shared<Woman>(b->getAge());
} else {
return b;
}
}
int main(){
std::shared_ptr<Human> x = std::make_shared<Child>;
x = x*19;
}
This doesn't seem like a good design though. A Human's status as a child or adult would be better represented as an attribute of the object or by a function that checks if age is greater than 18.
You cannot make the type T<Derived> inherit from T<Base> because C++ templates do not support covariance. To do so would be unsafe for certain types, such as mutable references to containers. (Imagine taking a reference to std::vector<Cat> as std::vector<Animal>& and pushing back a dog!)
(I would make this answer a comment, but I don't have comment abilities.)
Update:
You can write a non-template wrapper that handles heap data:
class Wrapper
{
public:
Wrapper(Base* b) : raw(b) {}
~Wrapper() { delete raw; }
Base& get() { return *base; }
private:
Base* raw;
}
Of course, in your example, you use std::shared_ptr and not std::unique_ptr. You would have to handle reference counting instead of simply deleting the data in the destructor, but the technique of keeping an internal raw pointer still stands.
Update 2:
The above code could be used as is to provide a level of indirection, such that all classes that inherit from the base class may be held in the same type, without writing your own reference counter:
std::shared_ptr<Wrapper>
This solution may be seen as similar to doing std::shared_ptr<Base*>, except that the latter solution would leak memory.

Better way to store inherited class to the file

This code works, but what is the correct way of doing this?
I mean, how can I eliminate the switch statement in read_in function, or to handle all reading in animal class, or its sub-classes, so my read_in function can be as simple as my write_out function?
I have a vector<animal*> *animals filled with cats and generic animals that I need to write/read to/from a file.
I have omitted some code, so the post doesn't get too big...
enum class animal_type
{
GENERIC_ANIMAL,
CAT
};
Suppose I have a class animal
class animal
{
animal_type m_type;
string m_name;
virtual void write_binary(ofstream &out)
{
out.write((char*)(&m_type), sizeof(m_type)); //first 'animal_type'
out.write((char*)(&m_type), sizeof(m_type)); //second 'animal_type'
out.write(m_name.c_str(), m_name.size()+1);
{
virtual void read_binary(std::ifstream &in)
{
in.read((char*)(&m_type), sizeof(m_type)); //read the second animal type here
m_name = read_null_string(in);//this function returns next string from input
}
};
and a class that derives from animal
class cat : public animal
{
bool m_is_cute;
void write_binary(std::ofstream &out)
{
animal::write_binary(out);
out.write((char*)(&m_is_cute), sizeof(m_is_cute));
}
void read_binary(std::ifstream &in)
{
animal::read_binary(in);
in.read((char*)(&m_is_cute), sizeof(m_is_cute));
}
};
I write them to a file like this
void write_out(std::ofstream &out, std::vector<animal*> *animals)
{
int size = animals->size();
out.write((char*)(&size), sizeof(size));
for(animal* a : *animals)
{
a->write_binary(out);
}
}
And read them from the file like this
void read_in(std::ifstream &in, std::vector<animal*> *animals)
{
animals->clear();
int size;
in.read((char*)(&size), sizeof(size));
for(int i = 0; i< size; ++i)
{
animal_type type;
//read the first 'animal_type' here
in.read((char*)(&type), sizeof(type));
animal *a;
switch(type)
{
case(animal_type::GENERIC_ANIMAL):
a = new animal(in);//this constructor just calls the read_binary method
break;
case(animal_type::CAT):
a = new cat(in);//this constructor just calls the read_binary method
break;
}
animals->push_back(a);
}
}
Here is one (of many) options:
First, we change enum class animal_type to a plain old enum becaysuse we want to use it as an integer. If this is not allowed, leave it enum class animal_type and use a static_cast<size_t> to convert it later.
enum animal_type
{
GENERIC_ANIMAL = 0, // must be 0. First index in array
CAT,
LAST, // must be last. Put no animals after it
FIRST = GENERIC_ANIMAL // makes it easy to loop FIRST to LAST
};
Then define all of your animals.
Next, build an array of functions that call the appropriate animal constructors. This array must exactly match the enum above. If this is a concern, consider using std::map instead.
std::function<animal *(std::ifstream &)> animalFactory[] =
{
[](std::ifstream & in) {return new animal(in);},
[](std::ifstream & in) {return new cat(in);}
};
Documentation on std::function
Documentation on Lambda expressions
Next define exceptions to be thrown should bad things happen. The could be as simple as
class Bogus_File_Exception: public std::exception
{
const char* what() const
{
return "File read failed.";
}
};
Finally read_in becomes
void read_in(std::ifstream &in, std::vector<animal*> & animals) // note the reference
{
animals.clear();
int size;
if (in.read((char*)(&size), sizeof(size))) // testing for successful read
{
for(int i = 0; i< size; ++i)
{
animal_type type;
if (in.read((char*)(&type), sizeof(type))) // testing again
{
if (type < animal_type::LAST)
{
animals.push_back(animalFactory[type](in));
}
else
{
throw Unknown_Animal_Exception();
}
}
else
{
throw Bogus_File_Exception();
}
}
}
else
{
throw Bogus_File_Exception();
}
}

C++ declare derived class object inside of if-else and use it outside

I have a (parent) class named Alma with the (virtual) function Getwidth() and two derived class of Alma, named Birs (with the special function Getheight()) and Citrom (with the special function Getdepth()). I want to declare an object - named Attila - which type is Birs or Citrom depending on a bool. Later, I want to use the common function Getwidth() and also the special functions (depending the bool mentioned).
My (not working) code:
/*...*/
/*Classes*/
class Alma{
public: virtual int Getwidth() = 0;
/*ect...*/
}
class Birs: public Alma{
int Getwidth(){return 1;}
public: int Getheight(){return 2;}
/*ect...*/
}
class Citrom: public Alma{
int Getwidth(){return 3;}
public: int Getdepth(){return 4;}
/*ect...*/
}
/*...*/
/*Using them*/
void Useobjects(){
/*Create object depending on bool*/
if(b00lvar){
Birs Andor();
std::cout<<Andor.Getwidth()<<" "<<Andor.Getheight()<<std::endl;
}else{
Citrom Andor();
std::cout<<Andor.Getwidth()<<" "<<Andor.Getdepth()<<std::endl;
}
/*Using the common part of object*/
std::cout<<Andor.Getwidth()<<std::endl;
/*Using the special part of object*/
if(b00lvar){
std::cout<<Andor.Getheight()<<std::endl;
}else{
std::cout<<Andor.Getdepth()<<std::endl;
}
/*ect...*/
}
This is a classic case of polymorphic object handling. Just make sure you are familiar with that concept as well with pointers and references.
What you need is something looking like:
Alma* Andor;
if(b00lvar){
Andor = new Birs();
std::cout<<Andor->Getwidth()<<" "<<Andor->Getheight()<<std::endl;
}else{
Andor = new Citrom();
std::cout<<Andor->Getwidth()<<" "<<Andor->Getdepth()<<std::endl;
}
Next use dynamic_cast to get back to the derived types and finally of course do not forget to delete the object. But first read about those concepts.
You cannot define a single object whose type is this or that, depending on something else. C++ doesn't work this way. C++ is a statically-typed language. This means that the type of every object is determined at compile time. Other languages, like Perl, or Javascript, are dynamically-typed, where the type of an object is determined at runtime, and a single object can be one thing, at one point, and something else at a different point.
But C++ does not work this way.
To do something like what you're trying to do, you have to refactor the code, and work with the virtual superclass. Something like this:
void UseObject(Alma &andor)
{
/*Using the common part of object*/
std::cout<<andor.Getwidth()<<std::endl;
/*Using the special part of object*/
/* This part is your homework assignment */
}
void Useobjects(){
/*Create object depending on bool*/
if(b00lvar){
Birs andor;
std::cout<<Andor.Getwidth()<<" "<<Andor.Getheight()<<std::endl;
UseObject(andor);
}else{
Citrom andor;
std::cout<<Andor.Getwidth()<<" "<<Andor.Getdepth()<<std::endl;
UseObject(andor);
}
}
Another approach would be to use two pointers, in this case passing two pointers to UseObject(). One of the two pointers will always be a nullptr, and the other one a pointer to the instantiated object, with UseObject() coded to deal with whatever object is passed in.
That's also possible, but will result in ugly code, and if I was an instructor teaching C++, I would mark down anyone who handed in code that did that.
If the type of the object (Alma or Citrom) is decided at the startup, then it's a classic polymorphism, as other answers described:
https://stackoverflow.com/a/36218884/185881
What're you missing from your design is, to name the common ancestor with common behaviors (e.g. Gyumolcs).
If the object should once act as Alma and other times as Citrom, you should implement a single class, which have a flag or enum (ACT_AS_CITROM, ACT_AS_ALMA), or, if the behavior is limited to one method, then it should have a parameter, which tells which action to perform (alma-like or citrom-like).
You can do this with pointer semantic and type introspection with dynamic_cast. I extended your example to show how I would approach it.
Here is the Demo
#include <iostream>
#include <memory>
using namespace std;
class Alma{
public:
virtual int Getwidth() = 0;
};
class Birs: public Alma{
public:
int Getwidth() { return 1; }
int Getheight() { return 2; }
};
class Citrom: public Alma{
public:
int Getwidth() { return 3; }
int Getdepth() { return 4; }
};
shared_ptr<Alma> make_attila(bool birs)
{
if (birs)
return make_shared<Birs>();
else
return make_shared<Citrom>();
}
void test_attila(shared_ptr<Alma> attila)
{
cout << "width: " << attila->Getwidth() << "\n";
if (auto as_birs = dynamic_pointer_cast<Birs>(attila))
cout << "height: " << as_birs->Getheight() << "\n";
else if (auto as_citrom = dynamic_pointer_cast<Citrom>(attila))
cout << "depth: " << as_citrom->Getdepth() << "\n";
}
int main() {
shared_ptr<Alma> attila = make_attila(true);
test_attila(attila);
attila = make_attila(false);
test_attila(attila);
return 0;
}
Next step would be to make make_attila a template function taking the Derived class as a template parameter instead of a bool.
template <class Derived>
shared_ptr<Alma> make_attila()
{
return make_shared<Derived>();
}
Two things:
If you want to use it outside the if, you will have to declare it outside the if.
You need references or pointers for this kind of polymorphism.
unique_ptr<Alma> Andor;
if (b00lvar) {
Andor = make_unique<Birs>();
} else {
Andor = make_unique<Citrom>();
}
std::cout << Andor->Getwidth() << std::endl;
Some other answer suggested using shared_ptr but that's overkill here. 99% of the time unique_ptr is sufficient.
Polymorphism isn't always the way to go if an object is known to be either a B or a C. In this case, a boost::variant is often more succinct.
Having said this, if you want to go down the polymorphic route it's important to remember something that will guide the design.
Polymorphic means runtime polymorphic. I.e. the program cannot know the real type of the object. It also cannot know the full set of possible types the object could be, since another developer could manufacture a type that your module's code knows nothing about. Furthermore, when using the Alma interface, the code should not need to know anything more. Invoking magic such as "I know it'll be a Citrom because the bool is true" is laying the foundations for a code maintenance nightmare a few weeks or months down the line. When done in commercial, production code, it results in expensive and embarrassing bug-hunts. Don't do that.
This argues that all relevant information about any object of type Alma must be available in the Alma interface.
In our case, the relevant information is whether it has the concept of height and/or depth.
In this case, we should probably include these properties in the base interface plus provide functions so that the program can query whether the property is valid before using it.
Here is something like your example written this way:
#include <iostream>
#include <memory>
#include <typeinfo>
#include <string>
#include <exception>
#include <stdexcept>
// separating out these optional properties will help me to reduce clutter in Alma
struct HeightProperty
{
bool hasHeight() const { return impl_hasHeight(); }
int getHeight() const { return impl_getHeight(); }
private:
// provide default implementations
virtual bool impl_hasHeight() const { return false; }
virtual int impl_getHeight() const { throw std::logic_error("getHeight not implemented for this object"); }
};
struct DepthProperty
{
bool hasDepth() const { return impl_hasDepth(); }
int getDepth() const { return impl_getDepth(); }
private:
virtual bool impl_hasDepth() const { return false; }
virtual int impl_getDepth() const { throw std::logic_error("getDepth not implemented for this object"); }
};
class Alma : public HeightProperty, public DepthProperty
{
public:
Alma() = default;
virtual ~Alma() = default;
// note: nonvirtual interface defers to private virtual implementation
// this is industry best practice
int getWidth() const { return impl_getWidth(); }
const std::string& type() const {
return impl_getType();
}
private:
virtual int impl_getWidth() const = 0;
virtual const std::string& impl_getType() const = 0;
};
class Birs: public Alma
{
private:
// implement the mandatory interface
int impl_getWidth() const override { return 1; }
const std::string& impl_getType() const override {
static const std::string type("Birs");
return type;
}
// implement the HeightProperty optional interface
bool impl_hasHeight() const override { return true; }
int impl_getHeight() const override { return 2; }
};
class Citrom: public Alma
{
private:
// implement the mandatory interface
int impl_getWidth() const override { return 3; }
const std::string& impl_getType() const override {
static const std::string type("Citrom");
return type;
}
// implement the DepthProperty optional interface
bool impl_hasDepth() const override { return true; }
int impl_getDepth() const override { return 4; }
};
/*...*/
/*Using them*/
// generate either a Birs or a Citrom, but return the Alma interface
std::unique_ptr<Alma> make_alma(bool borc)
{
if (borc) {
return std::make_unique<Birs>();
}
else {
return std::make_unique<Citrom>();
}
}
void Useobjects()
{
for (bool b : { true, false })
{
std::unique_ptr<Alma> pa = make_alma(b);
std::cout << "this object's typeid name is " << pa->type() << std::endl;
std::cout << "it's width is : " << pa->getWidth() << std::endl;
if(pa->hasHeight()) {
std::cout << "it's height is: " << pa->getHeight() << std::endl;
}
if(pa->hasDepth()) {
std::cout << "it's depth is: " << pa->getDepth() << std::endl;
}
}
}
int main()
{
Useobjects();
return 0;
}
expected output:
this object's typeid name is Birs
it's width is : 1
it's height is: 2
this object's typeid name is Citrom
it's width is : 3
it's depth is: 4

Print out the values stored in vars of different classes, that have the same ancestor

I have this class:
class CComputer {
public:
// constructor
CComputer(string name) {
this->name = name;
};
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component const & component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
string name;
string address;
list<Component> listOfComponents;
};
and then these classes:
// ancestor for other classes...It's really dummy yet, but I dunno what to add there
class Component {
public:
Component() {};
~Component() {};
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) {
this->mem = mem;
};
int mem;
};
Now I feed my CComputer class with some values:
CComputer c("test.com");
c . AddAddress("123.45.678.910") .
AddComponent(CCPU(8, 2400)) .
AddComponent(CCPU(8, 1200)).
AddComponent(CMemory(2000)).
AddComponent(CMemory(2000)));
And now I would like to print it out with all the info I've put in there (CCPU & CMemory details including)
but how to implement it, to be able to iterate through CComputer::listOfComponents and don't care if I acctually access CCPU or CMemory ? I can add it to that list, but I have really no idea, how to make it, to be able to access the variables of those components.
So the output should look like:
##### STARTING #####
CComputer:
name:test.com
address:123.45.678.910
CCPU:
cores:8,freq:2400
CCPU:
cores:8, freq:1200
CMemory:
mem:2000
CMemory:
mem:2000
###### FINISHED! #####
As others have mentioned, you need to implement a virtual function (e.g. virtual std::string ToString() const = 0;) in the base class that is inherited and overridden by each child class.
However, that isn’t enough. Your code exhibits slicing which happens when you copy your child class instances into the list: the list contains objects of type Component, not of the relevant child class.
What you need to do is store polymorphic instances. Values themselves are never polymorphic, you need to use (smart) pointers or references for this. References are out, however, since you cannot store them in a standard container (such as std::list). Using raw pointers is considered bad style nowadays, but judging from the naming conventions of your classes you don’t learn modern C++ in your class (sorry!).
Therefore, raw pointers is probably the way to go. Change your code accordingly:
Store a list of pointers:
list<Component*> listOfComponents;
Make the argument type of AddComponent a pointer instead of const&.
Call the function by passing a newed object, e.g.:
AddComponent(new CCPU(8, 2400))
Now your code leaks memory left, right and center. You need to implement a destructor to free the memory:
~CComputer() {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i)
delete *i;
}
But now your code violates the Rule of Three (read this article! It’s important, and it may be the most useful thing about C++ you’re going to learn in this programming class) and consequently you also need to implement the copy constructor and copy assignment operator. However, we can’t. Sorry. In order to implement copying for your class, you would have to implement another virtual function in your Component class, namely one that clones an object (virtual Component* Clone() const = 0;). Only then can we proceed.
Here’s a sample implementation in CCPU:
Component* Clone() const {
return new CCPU(cores, freq);
}
… this needs to be done in all classes deriving from Component, otherwise we cannot correctly copy an object of a type that derives from Component and is hidden behind a pointer.
And now we can implement copying in the CComputer class:
CComputer(CComputer const& other)
: name(name)
, address(addess) {
typedef std::list<Component*>::iterator iter_t;
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
}
CComputer& operator =(CComputer const& other) {
if (this == &other)
return *this;
name = other.name;
address = other.address;
listOfComponents.clear();
for (iter_t i = other.listOfComponents.begin(); i != other.listOfComponents.end(); ++i)
listOfComponents.push_back((*i)->Clone());
return *this;
}
This code is brittle, not thread-safe and error-prone and no competent C++ programmer would ever write this1. Real code would for instance use smart pointers instead – but as mentioned before I’m pretty sure that this would be beyond the scope of the class.
1 What does this make me now, I wonder?
Just add a virtual method to Class Component called e.g. toString(), which returns a string describing the component. Then you can iterate through all components and call toString() without worrying about exactly what each component is. If you do that, then for each computer you would be able to print out the values of all the components.
However, as pointed out in one of the comments, the example output you give in the question outputs the CCPU for all computers, then all the memory for all computers. To order the output like that, you'll need to add another virtual method to Component called e.g. getType() which returns an enum or integer that represents the type of the information. You can then have two for-next loops, one nested inside the other, where the outer loop iterates through all the types and the inner loop iterating through all the computers calling the toString() on all components which match the type specified in the outer for loop.
Here's something that implements this idea.
#include <iostream>
#include <string>
#include <list>
using namespace std;
int const TYPE_CCPU = 1;
int const TYPE_MEMORY = 2;
class Component {
public:
virtual int GetType() { return -1; }
virtual std::string ToString() const {
return "OOPS! Default `ToString` called";
}
};
class CComputer {
public:
typedef std::list<Component*>::iterator iter_t;
// constructor
CComputer(string name) {
this->name = name;
};
~CComputer() {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
delete *i;
}
}
// overloaded operator << for printing
friend ostream& operator<<(ostream& os, const CComputer& c);
// adds some component for this computer
CComputer & AddComponent(Component *component) {
this->listOfComponents.push_back(component);
return *this;
};
// sets address for this computer
CComputer & AddAddress(const string & address) {
this->address = address;
return *this;
};
void PrintType(int type) {
for (iter_t i = listOfComponents.begin(); i != listOfComponents.end(); ++i) {
if ((*i)->GetType() == type)
std::cout << (*i)->ToString() << '\n';
}
}
string name;
string address;
list<Component*> listOfComponents;
};
class CCPU : public Component {
public:
CCPU(int cores, int freq) {
this->cores = cores;
this->freq = freq;
};
int GetType() { return TYPE_CCPU; }
std::string ToString() const {
return "CCPU::ToString()";
}
int cores;
int freq;
};
class CMemory : public Component {
public:
CMemory(int mem) { this->mem = mem; };
int GetType() { return TYPE_MEMORY; }
std::string ToString() const {
return "CMemory::ToString()";
}
int mem;
};
typedef std::list<CComputer*>::iterator iter_c;
int main() {
list<CComputer*> computerlist;
CComputer *c1 = new CComputer("test.com"), *c2 = new CComputer("test2.com");
c1->AddAddress("123.45.678.910").
AddComponent(new CCPU(8, 1200)).
AddComponent(new CMemory(2000));
computerlist.push_back(c1);
c2->AddAddress("987.65.432.10").
AddComponent(new CCPU(8, 2400)).
AddComponent(new CMemory(4000));
computerlist.push_back(c2);
for(int t=TYPE_CCPU; t<=TYPE_MEMORY; t++)
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
(*i)->PrintType(t);
}
for (iter_c i = computerlist.begin(); i != computerlist.end(); ++i) {
delete (*i);
}
}
Implement ToString() in each of your classes. In .NET this is a standard even the "object" type implements.