Inheritance, defining a function for creating an object of a specified type? - c++

Title does not help im sure.
Anyway, at the moment i'm working with the following
http://puu.sh/7wJed.png
Everything's fine and inherited correctly, however, in order to create an object of say 'aircraftCarrier' i'd need to pass the 12 values + the two inherited values every-time i want to use a function such as
generateAirCraftCarrier(1,2,3,4,5,6,7,8,9,10,11,12);
I could simply pass in a navalVessel instance into the function instead, such that
generateAirCraftCarrier(myNavalVessel, inherit var 1, inherit var 2);
BUT this would not be entirely a solution because what happens when the aircraft carrier has a different 'Speed' for example?
can i have option parameters, which if null use the myNavalVessel object? Looking for some guidance here, sorry about the gibberish.

Why do you need one function to define all 12 values on an AircraftCarrier? Why not build it up with a number of setters on AircraftCarrier and NavalVessel? e.g:
class NavalVessel {
float speed_;
public:
void setSpeed(float speed) { speed_ = speed; }
};
class AircraftCarrier : public NavalVessel {
int noHeliPads_;
int noRunways_;
public:
void setNoHeliPads(int noHeliPads) { noHeliPads_ = noHeliPads; }
void setNoRunways(int noRunways) { noRunways_ = noRunways; }
};
int main() {
AircraftCarrier aircraftCarrier;
aircraftCarrier.setSpeed(25.3);
aircraftCarrier.setNoHeliPads(3);
aircraftCarrier.setNoRunways(2);
}

Could named parameters idiom be useful for you?
class AircraftCarrierParameters;
class AircraftCarrier
{
private:
AircraftCarrierParameters _params;
public:
AircraftCarrier(const AircraftCarrierParameters& params)
: _params(params) {}
AircraftCarrierParameters params() const { return _params;}
};
class AircraftCarrierParameters
{
private:
double _speed;
int _someOtherStuff;
public:
AircraftCarrierParameters()
: _speed(0) //default parameters
, _someOtherStuff(0)
{
}
double speed() const { return _speed; }
double someOtherStuff() const { return _someOtherStuff; }
AircraftCarrierParameters& setSpeed(double speed) { _speed = speed; return *this; }
AircraftCarrierParameters& setSomeOtherStuff(double stuff) { _someOtherStuff = stuff; return *this; }
};
AirCraftCarrier generateAirCraftCarrier(const AircraftCarrierParameters& params)
{
//...
}
void main()
{
AircraftCarrier c1(AircraftCarrierParameters());
AircraftCarrier c2(c1.params().setSpeed(30));
}

Related

C++,runtime error: member call on null pointer of type

First of all, I am new here to c++ and I'm trying to learn it. Also new to stackoverflow.
Find it quite hard to be honest.
If you have additional comments in terms of my code and how i can improve it, please let me know as I am still in the learning process.
ok I was I just creating a online booking system using object oriented programming.
Ok so the main issue is that I dont understand why system.setDisplay(1234); isn't printing anything. I have tried everything and just isn't adding up.
OnlineBookingSystem is the class is being used to call setDisplay(id) which then invoked the display class.
If you can help it would mean the world to me and the error i`m getting is:
runtime error: member call on null pointer of type 'User' (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:179:54
#include <vector>
#include <string>
#include <iostream>
#include <memory>
#include <queue>
using namespace std;
enum class BookGenre
{
Horror,Adventure,Romance,Comic
};
class Book
{
private:
BookGenre genre;
string title;
size_t id;
public:
Book(string title,size_t id,BookGenre genre):title(title),id(id),genre(genre){}
string getTitle(){return title;}
size_t getId(){return id;}
BookGenre getGenre(){return genre; }
};
class Library
{
private:
vector<shared_ptr<Book>> listOfBooks;
public:
Library(){};
void addBook(string title,size_t id,BookGenre genre)
{
listOfBooks.push_back(make_shared<Book>(title,id,genre));
}
shared_ptr<Book> getBook(size_t id)
{
for(auto&x:listOfBooks)
{
if(x->getId()==id)
{
return x;
}
}
return nullptr;
}
void removeBook(size_t id)
{
for(auto it=listOfBooks.begin();it!=listOfBooks.end();it++)
{
if((*it)->getId()==id)
{
listOfBooks.erase(it);
}
}
}
};
class User
{
protected:
size_t id;
string username;
public:
User(size_t id,string username):id(id),username(username)
{
}
virtual ~User(){}
size_t getId(){return id;}
string getUsername(){return username;}
};
class Employee:public User{
private:
double salary;
public:
Employee(size_t id,string username,double salary):User(id,username),salary(salary)
{
}
void setSalary(double salary)
{
this->salary=salary;
}
double getSalary(){return salary;}
};
class Customer:public User{
private:
bool membership;
public:
Customer(size_t id,string username):User(id,username)
{
membership=false;
}
void setMemberActive()
{
membership=true;
}
bool isMemberActive()
{
return membership;
}
};
class UserManager
{
private:
vector<shared_ptr<User>>listOfUsers;
queue<shared_ptr<Customer>>queue;
public:
UserManager()
{
}
void addCustomer(size_t id,string username)
{
listOfUsers.push_back(make_shared<Customer>(id,username));
}
void removeCustomer(string username)
{
for(auto it=listOfUsers.begin();it!=listOfUsers.end();it++)
{
if(dynamic_pointer_cast<Customer>(*it))
{
if((*it)->getUsername()==username)
{
listOfUsers.erase(it);
}
}
}
}
shared_ptr<Customer> getCustomer(string username)
{
for(auto it=listOfUsers.begin();it!=listOfUsers.end();it++)
{
if(dynamic_pointer_cast<Customer>(*it))
{
if((*it)->getUsername()==username)
{
return dynamic_pointer_cast<Customer>(*it);
}
}
}
return nullptr;
}
void addToQueue(string username)
{
queue.push(getCustomer(username));
}
void removeCurrentCustomer()
{
queue.pop();
}
shared_ptr<Customer> getNextCustomer()
{
if(queue.empty())
{
return nullptr;
}
return queue.front();
}
/*
same process for user;
*/
};
class Display
{
private:
shared_ptr<Customer> m_customer;
shared_ptr<Book> m_book;
public:
Display(shared_ptr<Customer> _customer,shared_ptr<Book> _book ):m_customer(_customer),m_book(_book)
{
}
shared_ptr<Customer> getUser(){return m_customer;}
shared_ptr<Book> getBook(){return m_book;}
void displayInfo()
{
cout<<"Customer username: "<<m_customer->getUsername()<<endl;
cout<<"Member Active: "<<m_customer->isMemberActive();
cout<<"book id: "<<m_book->getId()<<endl;
cout<<"book title: "<< m_book->getTitle()<<endl;
}
};
class OnlineBookingSystem
{
private:
UserManager manager;
Library library;
shared_ptr<Display>display;
public:
OnlineBookingSystem()
{
UserManager manager;
Library library;
this->manager=manager;
this->library=library;
this->display=nullptr;
}
Library getLibrary()
{
return library;
}
UserManager getUserManager()
{
return manager;
}
void setDisplay(size_t id)
{
display=make_shared<Display>( manager.getNextCustomer(),library.getBook(id));
display->displayInfo();
}
shared_ptr<Display> getDisplay()
{
return this->display;
}
};
int main()
{
OnlineBookingSystem system;
auto lib=system.getLibrary();
lib.addBook("Adventure of Pablo",1234,BookGenre::Adventure);
auto manager=system.getUserManager();
manager.addCustomer(2020,"Michael");
auto _customer= manager.getCustomer("Michael");
_customer->setMemberActive();
manager.addToQueue("Michael");
system.setDisplay(1234);
return 0;
}
Problems I see:
Problem 1
Since the return type of OnlineBookingSystem::getLibrary() is Library, the line
auto lib=system.getLibrary();
construct lib as a copy of the object in system. Any changes made to lib are changes to the copy, not to the Library object in system.
To fix the problem, change the return type to a reference:
Library& getLibrary()
{
return library;
}
and capture the return value also as a reference in main.
auto& lib=system.getLibrary();
Problem 2
Similar to Problem 1 but this time it is in OnlineBookingSystem::getUserManager. Change its return type to be a reference:
UserManager& getUserManager()
{
return manager;
}
and capture the return value also as a reference in main.
auto& manager=system.getUserManager();
Problem 3
Adopt defensive programming at every step until something is a performanced bottleneck. If a return value of a function can be nullptr, check the return value at the point of invocation and deal with the case when the return value is indeed nullptr.
Update OnlineBookingSystem::setDisplay to:
void setDisplay(size_t id)
{
display=make_shared<Display>( manager.getNextCustomer(),library.getBook(id));
if ( display )
{
display->displayInfo();
}
else
{
// Deal with the nullptr case
std::cout << "Unable to find a book with id " << id << std::endl;
}
}
The problem
It comes down to your call to system.getUserManager() returning a copy of OnlineBookingSystem's manager field, instead of a pointer to it.
As a result, when you call manager.addToQueue("Michael"), it is adding Michael to the UserManager instance that is local to int main(), instead of the instance that's held in system.
Your function OnlineBookingSystem::setDisplay(size_t) makes a call to manager.getNextCustomer(). Because you've added Michael to a different instance of manager, this returns nullptr.
How to fix it
You simply need to modify OnlineBookingSystem::getUserManager() to return a pointer to manager instead of a copy of it:
UserManager* getUserManager()
{
return &manager;
}
Then modify the calling code to use the pointer dereference operator (->) instead of a period to access methods on manager.

base class reference to derived object - strategy pattern

Below is a simple strategy pattern implemented using the base class references to a derived object. The solution does not produce an expected result (12 and 2). When base class reference is switched to pointers, it works. Can someone explain what is happening behind the scenes with references here? The issue is in the setStrategy() method of the Context class. I am wondering why doesn't the strategy variable reference the ConcreteStrategy2 after the call to setStrategy() method?
#include <iostream>
class Strategy {
public:
virtual ~Strategy() = default;
virtual int execute(int x, int y) const = 0;
};
class ConcreteStrategy1 : public Strategy {
public:
int execute(int x, int y) const override
{
return x + y;
}
};
class ConcreteStrategy2 : public Strategy {
public:
int execute(int x, int y) const override
{
return x - y;
}
};
class Context {
Strategy &strategy;
public:
Context(Strategy &strategy) : strategy {strategy}
{
}
void setStrategy(Strategy &strat)
{
this->strategy = strat;
}
void doLogic() const
{
std::cout << strategy.execute(7, 5) << std::endl;
}
};
int main()
{
ConcreteStrategy1 strat;
Context context {strat};
context.doLogic();
ConcreteStrategy2 strat2;
context.setStrategy(strat2);
context.doLogic();
return 0;
}
You are trying to reassign a reference, but references cannot be reassigned. It assigns to Strategy object being referred to instead. If you make Strategy non-copyable/assignable that re-assignment through reference will fail to compile.
Use a pointer instead:
class Context {
Strategy* strategy;
public:
Context(Strategy &strategy) : strategy {&strategy} {}
void setStrategy(Strategy &strat) { this->strategy = &strat; }
void doLogic() const { std::cout << strategy->execute(7, 5) << std::endl; }
};
Using reference members is almost always a mistake because it breaks value semantics, as you observe. One can get away with using reference members in non-copyable classes.
Additional solution to Maxim Egorushkins answer.
You could also use std::reference_wrapper instead of a pointer:
class Context {
std::reference_wrapper<Strategy> strategy;
public:
Context(Strategy &strategy) : strategy {strategy} { }
void setStrategy(Strategy &strat) { this->strategy = strat; }
void doLogic() const { std::cout << strategy.get().execute(7, 5) << std::endl; }
};
Here are both versions next to each other:
https://gcc.godbolt.org/z/dveK9T

Override virtual function with more parameters

I have an array of happy people.
Each happy person has a virtual function called updateHappiness() which is used to update their happiness attribute.
Each person likes their own thing.
Rain lovers are happy persons who really like hearing the sound of the rain and it increases their happiness level. They inherit from the happy person class.
As a consequence, they need to know when it is raining while updating their happiness by overloading the updateHappiness() function with updateHappiness(bool isRaining) as in this post : overload virtual function with different parameters in c++, however, this is a problem because there are many kinds of people and we would like to update them all by calling the same function for every person.
We could have the parameter stored inside of the person class and pass it in the class constructor as in this post : Override number of parameters of pure virtual functions however rain is not a constant state and we would have to call a function updateRainState(bool isRaining) which would cause the same problem as before.
We could pass the parameter bool isRaining to every person even though they don't care about the rain but it would also be a problem because some people like the rain, some people like seeing the daylight, some people like it when their friends are happy... so it would add many useless parameters and it seems like a waste.
Finally, the best solution I could think of is to have a static function in the weather class to get the rain state without passing it as a parameter but it would look like a global variable and some people say that it is really bad!
What would you do to solve this problem ?
Here is an example code of what the classes are like :
class HappyPerson
{
public:
HappyPerson(): m_happinness(0) {}
virtual void updateHappinness() { m_happinness++; }
protected:
int m_happinness;
};
class Weather
{
public:
static int isRaining() { return raining; }
private:
static bool raining;
};
bool Weather::raining(0);
class RainLover : public HappyPerson
{
public:
RainLover() : HappyPerson() {}
void updateHappinness() { m_happinness++; if (Weather::isRaining()) m_happinness++; }
};
int main()
{
std::vector<HappyPerson*> happyPeople;
happyPeople.push_back(new RainLover);
// ... add many other persons
std::vector<HappyPerson*>::iterator it;
for (it = happyPeople.begin(); it != happyPeople.end(); it++)
{
(*it)->updateHappinness();
}
}
You should consider taking a completely different approach - use event callbacks instead.
When something in particular changes, only interested people are affected, so you should not waste time and effect trying to passing around the change to everyone else.
If a person's happiness depends on the Weather, then have the person register for Weather change events.
If a person's happiness depends on another person's happiness, then have the person register for the other person's happiness change events.
And so on.
For example:
class HappyPerson;
class HappinessChangeListener
{
public:
void happinessChanged(HappyPerson *person, bool isHappier) = 0;
};
class HappyPerson
{
public:
HappyPerson();
virtual ~HappyPerson() {}
void updateHappiness(int amount);
void registerHappinessChangeListener(HappinessChangeListener *listener);
void unregisterHappinessChangeListener(HappinessChangeListener *listener);
);
protected:
int m_happinness;
std::vector<HappinessChangeListener*> happinessChangeListeners;
void happinessChanged(bool isHappier);
};
...
HappyPerson::HappyPerson()
: m_happinness(0)
{
}
void HappyPerson::updateHappiness(int amount)
{
if (amount != 0)
{
m_happinness += amount;
happinessChanged(amount > 0);
}
}
void HappyPerson::registerHappinessChangeListener(HappinessChangeListener *listener)
{
happinessChangeListeners.push_back(listener);
}
void HappyPerson::unregisterHappinessChangeListener(HappinessChangeListener *listener)
{
std::vector<HappinessChangeListener*>::iterator i = std::find(happinessChangeListeners.begin(), happinessChangeListeners.end(), listener);
if (i != happinessChangeListeners.end())
happinessChangeListeners.erase(i);
}
void HappyPerson::happinessChanged(bool isHappier)
{
for(std::vector<HappinessChangeListener*>::iterator i = happinessChangeListeners.begin(); i != happinessChangeListeners.end(); ++i)
i->happinessChanged(this, isHappier);
}
class Weather;
class WeatherChangeListener
{
public:
void weatherChanged(Weather *weather) = 0;
};
class Weather
{
public:
Weather();
void rainStarted();
void rainStopped();
bool isRaining();
void registerWeatherChangeListener(WeatherChangeListener *listener);
void unregisterWeatherChangeListener(WeatherChangeListener *listener);
protected:
bool m_raining;
std::vector<WeatherChangeListener*> weatherChangeListeners;
void weatherChanged();
};
...
Weather::Weather()
: m_raining(false)
{
}
void Weather::rainStarted()
{
if (!m_rRaining)
{
m_rRaining = true;
weatherChanged();
}
}
void Weather::rainStopped()
{
if (m_rRaining)
{
m_rRaining = false;
weatherChanged();
}
}
bool Weather::isRaining()
{
return m_raining;
}
void Weather::registerWeatherChangeListener(WeatherChangeListener *listener)
{
weatherChangeListeners.push_back(listener);
}
void Weather::unregisterWeatherChangeListener(WeatherChangeListener *listener)
{
std::vector<WeatherChangeListener*>::iterator i = std::find(weatherChangeListeners.begin(), weatherChangeListeners.end(), listener);
if (i != weatherChangeListeners.end())
weatherChangeListeners.erase(i);
}
void Weather::weatherChanged()
{
for(std::vector<WeatherChangeListener*>::iterator i = weatherChangeListeners.begin(); i != weatherChangeListeners.end(); ++i)
i->weatherChanged(this);
}
class RainLover : public HappyPerson, public WeatherChangeListener
{
public:
RainLover(std::shared_ptr<Weather> &weather);
~RainLover();
void weatherChanged(Weather *weather);
protected:
std::shared_ptr<Weather> m_weather;
};
...
RainLover::RainLover(std::shared_ptr<Weather> &weather)
: HappyPerson(), m_weather(weather)
{
m_weather->registerWeatherChangeListener(this);
}
RainLover::~RainLover()
{
m_weather->unregisterWeatherChangeListener(this);
}
void RainLover::weatherChanged(Weather *weather)
{
updateHappiness(weather->isRaining() ? 1 : -1);
}
class HappyLover : public HappyPerson, public HappinessChangeListener
{
public:
HappyLover(std::shared_ptr<HappyPerson> &person);
~HappyLover();
void happinessChanged(HappyPerson *person, bool isHappier);
protected:
std::shared_ptr<HappyPerson> m_person;
};
...
HappyLover::HappyLover(std::shared_ptr<HappyPerson> &person)
: HappyPerson(), m_person(person)
{
m_person->registerHappinessChangeListener(this);
}
HappyLover::~HappyLover()
{
m_person->unregisterHappinessChangeListener(this);
}
void HappyLover::happinessChanged(HappyPerson *person, bool isHappier)
{
updateHappiness(isHappier ? 1 : -1);
}
int main()
{
std::shared_ptr<Weather> weather(new Weather);
std::vector<std::shared_ptr<HappyPerson>> happyPeople;
happyPeople.push_back(std::shared_ptr<HappyPerson>(new RainLover(weather)));
// or: happyPeople.push_back(std::make_shared<RainLover>(weather));
happyPeople.push_back(std::shared_ptr<HappyPerson>(new HappyLover(happyPeople[0])));
// or: happyPeople.push_back(std::make_shared_ptr<HappyLover>(happyPeople[0]));
// ... add many other persons
weather->rainStarted();
...
weather->rainStopped();
...
}

C++: how to get element defined in a different function of same class?

I defined a class in the header file like this:
class myClass
{
public:
void test();
void train();
private:
bool check;
}
Then in the cpp file, I did this:
void myClass::test()
{
int count = 9;
//some other work
}
void myClass::train()
{
int newValue = count;
....
}
Then without surprise, I got an error saying count is not defined. So what I want to do is in my train function use the count value that is defined in the test. Is there any good way to do this without using any additional dependencies? Thank you.
Well yes. That's called a member variable. Exactly like your bool check;.
Do
private:
bool check;
int count;
and then use it directly in your functions.
void myClass::test()
{
count = 9;
//Same as this->count = 9;
}
void myClass::train()
{
int newValue = count;
//Same as int newValue = this->count;
}
In your example, when method test finishes its work, count variable does not exist anymore, so there's no way of accessing it. You have to ensure, that its lifetime will be long enough to be accessed from another place. Making it a class field solves the problem (this is what class fields are for :)).
Do it this way:
class myClass
{
public:
void test();
void train();
private:
bool check;
int count; // <- here
}
and then
void myClass::test()
{
count = 9;
//some other work
}
But that's not the only solution. You can do it in another way, say:
class myClass
{
public:
int test()
{
// do some work
return 9;
}
void train(int count)
{
int newValue = count;
}
}
// (somewhere)
myClass c;
int count = c.test();
c.train(count);
That all depends on what test, train and count are for...

Pointer gives me the address rather than the value;

class ZombieLand : public Singleton<ZombieLand>
{
DECLARE_SINGLETON(ZombieLand);
public:
MachineState* world[19][19];
bool map[19][19];
MachineState* getField(int x, int y)
{
return world[x][y];
}
void setWorld(MachineState state)
{
world[state.x][state.y] = &state;
map[state.x][state.y] = true;
}
};
struct MachineState
{
template <typename MachineTraits>
friend class Machine;
enum Facing { UP, RIGHT, DOWN, LEFT};
MachineState()
: m_ProgramCounter(1)
, m_ActionsTaken(0)
, m_Facing(UP)
, m_Test(false)
, m_Memory(nullptr)
,x(0)
,y(0)
,point1(25, 10)
,point2(10, 40)
,point3(40, 40)
{ }
int m_ProgramCounter;
int m_ActionsTaken;
Facing m_Facing;
bool m_Test;
bool m_occupied;
int x;
int y;
Point point1;
Point point2;
Point point3;
int GetActionsPerTurn() const throw() { return m_ActionsPerTurn; }
int GetMaxMemory() const throw() {return m_MaxMemory; }
bool GetTruth() const throw() { return m_InfectOnAttack; }
void setPoint(Point p1, Point p2, Point p3)
{
point1=p1;
point2=p2;
point3=p3;
}
};
I later call the getField function by doing
MachineState *Field1 = ZombieLand::get().getField(state.x, state.y-1 );
The problem is that when i try to access a member by doing Field1->getTruth() it's returning me the address of the pointer rather than the actual value(false or true). I don't understand why this is happening
template <class T>
class Singleton
{
private:
static T* _instance;
protected:
Singleton() {}
public:
static T& get()
{
if (_instance)
{
return *_instance;
}
else
{
_instance = new T();
return *_instance;
}
}
};
if(ZombieLand::get().map[state.x+2][state.y] == true)
{
MachineState *field3 = ZombieLand::get().getField(state.x+2, state.y);
std::cout<<"FOUND FIELD"<<Field3->getTruth();
}
when this if statement becomes true it prints "FOUND FIELD 0246" onto my console
As we do not have the signature of both get and getField it is difficult to tell.
But perhaps try
*(ZombieLand::get().getField(state.x, state.y-1 ))
To get the value that the pointer is pointing to.
EDIT
It helps to read the code i.e.
MachineState * world [19][19];
is a 2D array of pointers. Nowhere in this code is those pointers given a value so as it stands you are just lucky that the thing does not just die.
Therefore,
MachineState *getField(int x, int y)
{
return world[x][y];
}
As specified by the signature of the function as well!
But where in this code do you give the pointer a value or meaning?
OK, now I think you've finally posted the code that has the problem
class ZombieLand : public Singleton<ZombieLand>
{
DECLARE_SINGLETON(ZombieLand);
public:
MachineState* world[19][19];
bool map[19][19];
MachineState* getField(int x, int y)
{
return world[x][y];
}
void setWorld(MachineState state)
{
world[state.x][state.y] = &state;
map[state.x][state.y] = true;
}
};
This is undefined behaviour because you are saving the pointer to a local variable state. The state variable gets destroyed after you have exited the setWorld function, so your world array is just holding pointers to destroyed objects, That's why you have garbage values.
Rewrite this class without pointers.
class ZombieLand : public Singleton<ZombieLand>
{
DECLARE_SINGLETON(ZombieLand);
public:
MachineState world[19][19];
bool map[19][19];
MachineState* getField(int x, int y)
{
return &world[x][y];
}
void setWorld(const MachineState& state)
{
world[state.x][state.y] = state;
map[state.x][state.y] = true;
}
};
Pointers are almost always a bad idea, you should try to write code without them.
Got there in the end at least.
void setWorld(MachineState state)
{
world[state.x][state.y] = &state;
map[state.x][state.y] = true;
}
You are initializing the contents of the world array with the memory address of a local variable on the stack that goes out of scope and is no longer valid after setWorld() exits, leaving the array pointing at invalid MachineState objects. When getField() is called later on, you get random data from the current call stack.
If you want the array to point to external MachineState instances, you need to pass them in by reference instead of by value so you get the address of the original object and not a temporary anymore:
void setWorld(MachineState &state)
{
world[state.x][state.y] = &state;
map[state.x][state.y] = true;
}
Otherwise, change the array to not hold pointers anymore:
class ZombieLand : public Singleton<ZombieLand>
{
DECLARE_SINGLETON(ZombieLand);
public:
MachineState world[19][19];
bool map[19][19];
MachineState* getField(int x, int y)
{
return &world[x][y];
}
void setWorld(const MachineState &state)
{
world[state.x][state.y] = state;
map[state.x][state.y] = true;
}
};