Encapsulation along with constructors - c++

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
}

Related

Should I create a new sub-class for any unique object which is the same as base class' object except for the default values?

I have a class:
Class Monster
{
private:
const int force;
const int prize;
const int damage;
}
In my code, I want to be able to easily create a unique Monster Object that I call Dragon which has default values for all the Monster's attributes that can't be initialized to different values (for example: all Dragons has force=5, prize=10, damage=10)
My question is- should Dragon be a subclass of Monster even though there is not realy a difference between the two except for the default values and the name of the object?
(In fact I have many types such as Dragon- each has it's own default values).
It seems that this is going to be a fine solution for my needs but I'm new to OOP and not sure it's acceptable or how to solve it differently, yet elegantly.
Although this might not be the cleanest solution, you could create an enum class for your different monster types and use a custom method to call the constructor.
enum class types {Dragon, Turtle, Squid};
class Monster {
private:
const int force;
const int prize;
const int damage;
public:
static Monster createMonster(types type) {
if (type == types::Dragon) {
return Monster(20, 10, 5);
}
else if (type == types::Squid) {
return Monster(5, 2, 3);
}
}
Monster(int f, int p, int d) : force(f), prize(p), damage(d){};
};
int main(void) {
Monster dragon1 = Monster::createMonster(types::Dragon);
Monster squid1 = Monster::createMonster(types::Squid);
return 0;
}

How should I call a player spell in a text RPG?

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.

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

How to transfer values set in privates of base class by an object of one derived class to an object of another derived class?

I have my main.cpp like this:
#include <iostream>
#include "curve1.h"
#include "curve2.h"
using namespace std;
int main()
{
Curve1 curve1Obj;
Curve2 curve2Obj;
curve1Obj.enterScores();
curve1Obj.calcAverage();
curve1Obj.output();
curve1Obj.curve();
curve1Obj.output(curve1Obj.new_getAverage1(), curve1Obj.new_getScore1());
curve2Obj.curve();
return 0;
}
Base class Score has two derived classes Curve1 and Curve2. There are two curve() functions, one is in Curve1 and other in Curve2 classes. getSize() returns the value of iSize.
My base class header score.h looks like this:
#ifndef SCORE_H
#define SCORE_H
class Score
{
private:
int *ipScore;
float fAverage;
int iSize;
public:
Score(
void enterScores();
void calcAverage();
void output();
void output(float, int*);
void setSize();
int getSize();
void setScore();
int *getScore();
float getAverage();
};
#endif
You can see that I have used curve1Obj to enter scores, calculate average and output. So if I call getSize() function with cuve1Obj, it gives the right size that I took from user in enterScores() function. Also the result is same if I call getSize() in score.cpp definition file in any of the functions (obviously).
.....
The problem is when I call curve() function of Curve2 class in main (line 23) with the object curve2Obj, it creates a new set of ipScore, fAverage and iSize (i think?) with garbage values. So when I call getSize() in curve() definition in curve2.cpp, it outputs the garbage.
.....
How can I cause it to return the old values that are set in curve1.cpp?
Here is my curve2.cpp
#include <iostream>
#include "curve2.h"
using namespace std;
void Curve2::curve()
{
cout << "getSize() returns: " << getSize() << endl; // out comes the garbage
}
Can I use a function to simply put values from old to new variables? If yes then how?
Well, basically your problem can't be easily solved the way it is.
Like you said:
1 - Don't use constructors of any type.
2 - Don't use vectors.
3 - Using dynamic new and delete etc. etc.
Use the constructors or stick with what G. Samaras and Richard Hodges said. You can only solve this that way.
There is limited information available here but I would say that your Score constructor has not initialised ipScore or iSize.
If you are hell-bent on using a pointer to a dynamically allocated array of ints for your score then at least null out the pointer in the constructor and test for null in the average function (i.e. no scores yet).
Better yet... use a std::vector of int for your scores.
Why are people still using new and delete? What the hell are they teaching in schools?
What I think you want is this:
#include <vector>
class Score {
public:
Score()
: _scores()
, _average(0)
{ }
void calcAverage() {
double total = 0;
if(auto s = _scores.size() > 0) {
for (const auto& v : _scores)
total += v;
total /= s;
}
_average = total;
}
virtual void curve() = 0;
protected:
// one of the few correct uses of 'protected' - giving limited access to data as interface to derived classes
const std::vector<double>& scores() const {
return _scores;
}
// or
std::vector<double> copyScores() const {
return _scores;
}
private:
// use doubles since you'll be doing floating point arithmetic
std::vector<double> _scores;
double _average;
};
class Curve1 : public Score {
public:
virtual void curve() override {
// custom curve function here
// written in terms of scores() or copyScores() if you want to make changes to the array
}
};
class Curve2 : public Score {
public:
virtual void curve() override {
// custom curve function here
// written in terms of scores();
}
};
You need to understand inheritance. Curve1 inherits from Score. Curve2 inherits from Score.
Now see this example:
#include <iostream>
class Base {
int x;
};
class A : public Base {
int a;
public:
void set_a(int arg) {
a = arg;
}
int get_a() {
return a;
}
};
class B : public Base {
int b;
public:
void set_b(int arg) {
b = arg;
}
int get_b() {
return b;
}
};
int main() {
A a_object;
a_object.set_a(4);
B b_object;
b_object.set_b(a_object.get_a());
std::cout << "a of a_object = " << a_object.get_a() << "\n";
std::cout << "b of b_object = " << b_object.get_b() << "\n";
return 0;
}
class A, has as members x and a. Class B has as members x and b.
When I create an instance of class A, I will two data members created internally, x and a.
When I create an instance of class A, I will two data members created internally, x and b.
But, the first x and the second are DIFFERENT. They are a different cell in the memory!
something like this:
class Score {
public:
Score()
: _scores(0)
, _size(0)
, _average(0)
{ }
// copy constructor
Score(const Score& rhs)
: _scores( new double[rhs._size] )
, _size(rhs._size)
, _average(rhs._average)
{
if (_size) {
for(int i = 0 ; i < _size ; ++i) {
_scores[i] = rhs._scores[i];
}
}
}
// ... and if copy constructor then always a copy operator
Score& operator=(const Score& rhs) {
// assignment in terms of copy constructor - don't repeat yourself
Score tmp(rhs);
swap(tmp);
return *this;
}
// pre c++11 we make our own swap.
// post c++11 we would make non-throwing move constructor and move-assignment operator
void swap(Score& rhs) {
// std::swap is guaranteed not to throw
std::swap(_scores, rhs._scores);
std::swap(_size, rhs._size);
std::swap(_average, rhs._average);
}
~Score()
{
delete[] _scores;
}
void calcAverage() {
double total = 0;
if(_size > 0) {
for (int i = 0 ; i < _size ; ++i)
total += _scores[i];
total /= _size;
}
_average = total;
}
virtual void curve() {};
private:
// use doubles since you'll be doing floating point arithmetic
double * _scores;
int _size;
double _average;
};
// rmember to override the copy operators and assignment operators of derived classes
// remember to call the base class's operator

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.