use of parent's function (inheritance c++) - c++

I have four .cpp files, Animal, Cattle, Sheep, DrugAdmin.
Animal is parent class of Cattle, and it has calcDose(), which calculates the amount of dose. DrugAdmin is main function.
The thing is, I want to use calcDose() function differently (Cattle, Sheep) and no calcDose() function is needed for Animal class. However, every time I try to use calcDose(), it automatically calls function in Animal class even when I want to use under Cattle class. This is the code I have done so far. (I've cut it down)
Animal.cpp
#include "Animal.h"
#include <string>
using namespace std;
Animal::Animal(int newid, double newweight, int yy, int mm, int dd, char newsex, vector<Treatment> treatArray)
{
id = newid;
weight = newweight;
yy = yy;
mm = mm;
dd = dd;
accDose = 0;
sex = newsex;
}
double Animal::calcDose(){
return 0;
}
Cattle.cpp
#include "Cattle.h"
using namespace std;
Cattle::Cattle(int newid, double newweight, int yy, int mm, int dd,
char newsex, vector<Treatment> newtreatArray, string newcategory)
: Animal(newid, newweight, yy,mm,dd, newsex, newtreatArray)
{
id = newid;
weight = newweight;
accDose = 0;
sex = newsex;
Cattle::category = newcategory;
}
Cattle::~Cattle(){}
double Cattle::calcDose(){
if(getDaysDifference() < 90 || getCategory() == "Meat"){
accDose = 0;
return accDose;
}
else if(getCategory() == "Dairy"){
if (weight < 250 || accDose > 200){
accDose = 0;
}
else{
accDose = weight * 0.013 + 46;
}
return accDose;
}
else if(getCategory() == "Breeding"){
if (weight < 250 || accDose > 250){
accDose = 0;
}
else{
accDose = weight * 0.021 + 81;
}
return accDose;
}
else
{
//cout << "It is not valid category" << endl;
}
}
Sheep class is pretty much same but the contents of calcDose()
DrugAdmin.cpp
#include "DrugAdmin.h"
using namespace std;
vector<Animal*> vec_Animal;
void addAnimal(){
int select=0;
int id;
double weight;
int yy;
int mm;
int dd;
char sex;
string category;
vector<Treatment> treatArray;
//user inputs all the values (i've cut it down)
Animal* c1 = new Cattle(id,weight,yy,mm,dd,sex,treatArray,category);
vec_Animal.push_back(c1);
}
void administerDose(int id) //Main Problem
{
vector<Animal*>::iterator ite_Animal = vec_Animal.begin();
for(ite_Animal; ite_Animal != vec_Animal.end(); ++ite_Animal)
cout<<"\nVector contains:"<< (*ite_Animal)->calcDose();
}
I'm sorry for the long and messed up question. Final question is, Cattle has extra data member which is category but the system doesn't recognise this as well. It recognises as if it is Animal object. Can I have a piece of advice please?
Cheers

every time I try to use calcDose(), it automatically calls function in Animal class
Sounds like you forgot to make calcDose a virtual member function.
Final question is, Cattle has extra data member which is category but the system doesn't recognise this as well. It recognises as if it is Animal object.
I'm not sure what you mean by the "system" "recognizing" anything, but if you access a Cattle object through an Animal*, then you can't get to the Cattle-specific members. You'd have use either a Cattle* or, again, polymorphism/virtual.

Unlike Java (and many other languages), C++ uses what is called "static binding" by default. Static binding means the function called is based on the declared type of the object (or pointer) at compile time, rather than what the object actually is (or what the pointer is actually pointing to).
The alternative to static binding is "dynamic binding". In dynamic binding, the function called is based on what the object actually is (or what the pointer is actually pointing to) at runtime.
To enable dynamic binding, which is what you'll want if you have an inheritance hierarchy set up and are maintaining base class pointers (Animal*s), you have to use the keyword virtual. virtual makes a function dynamically bound, so that calls to that function will reflect the actual runtime type of the object (Cattle) rather than the compile time declared pointer type (Animal*).
To declare a function virtual, you put the virtual keyword before the return value on the function declaration (in the class declaration / .h file):
class Animal
{
virtual void calcDose();
// ...
};
It is also good style to put virtual on the overridden function in the derived class (calcDose() in Cattle), although the virtual is implied for inherited functions, so putting it there is not strictly necessary.
The one last thing to make sure you to get dynamic binding, is to make sure you always have pointers (or references) to objects rather than objects on the stack (when you want dynamic binding). An object on the stack (i.e. Animal a) can only be it's declared type, it can't point to a derived class type. So the vector<Animal*> you have is perfect, because those pointers can point to Cattle objects (or Animal objects), while a vector could only hold actual animal objects.

You should make your calcDose function virtual and redefine it in each subclass: Cattle, etc. Also to recognize your Animal as a Cattle you have to cast it to Cattle class. But you are supposed to use the common base type interface most of the time.

Related

Dealing with errors due to different constructors in the code while allocating new memory in object oriented programming

I want to allocate new memory for my class which has some derived classes as well. as I have defined a constructor of type Professor(string name,int age,int publications,int cur_id) memory allocation
per[i] = new Professor; in the main throws error:no matching function for call to 'Professor::Professor().
another error I am getting is candidate: 'Professor::Professor(std::string, int, int, int) expects 4 arguments, 0 provided. please help me how to define a constructor which allocates memory without giving any error, thanks.
ps: I am trying to solve this question
part of my class looks like;
class Person{
protected:
string name;
int age;
public:
Person(string name,int age){
name=name;
age=age;
}
int z=0;
void getdata(){
string m;int n;
cin>>m>>n;
z++;
Person(m,n);
}
void putdata(){
cout<<name<<" "<<age<<endl;
}
};
class Professor: public Person{
public:
int publications;
int cur_id;
Professor(string name,int age,int publications,int cur_id)
:Person(name,age)
{
publications=publications;
cur_id=cur_id;
}
int b=0;
void getdata(){
string a;int b,c;
cin>>a>>b>>c;
b++;
Professor(a,b,c,b);
}
void putdata(){
cout<<name<<" "<<age<<" "<<publications<<" "<<cur_id<<endl;
}
};
class Student:public Person{
public:
int marks[6];
int cur_id;
Student(string name,int age,int arr[6],int cur_id)
:Person(name,age)
{
marks[6]=arr[6];
cur_id=cur_id;
}
int s=0;
void getdata(){
string p;int q;int r[6];
cin>>p>>q;
for(int i=0;i<6;i++){
cin>>r[i];
}
s++;
Student(p,q,r,s);
}
void putdata(){
cout<<name<<" "<<age<<" "<<marks[0]<<" "<<marks[1]<<" "<<marks[2]<<" "<<marks[3]<<" "<<marks[4]<<" "<<marks[5]<<" "<<cur_id<<endl;
}
};
My main function looks like
int main(){
int n, val;
cin>>n; //The number of objects that is going to be created.
Person *per[n];
for(int i = 0;i < n;i++){
cin>>val;
if(val == 1){
// If val is 1 current object is of type Professor
per[i] = new Professor;
}
else per[i] = new Student; // Else the current object is of type Student
per[i]->getdata(); // Get the data from the user.
}
for(int i=0;i<n;i++)
per[i]->putdata(); // Print the required output for each object.
return 0;
}
Your problem has nothing to do with dynamic allocations. You are trying to construct Professors and Students by calling their default constuctor (new Professor / new Student), but they don not have a default constructor.
A default constructor is a constructor that can be called without parameters. You can change existing constructors:
Person(string name = "",int age = 42) : name(name), age(age) {}
And similar for Student. Note that your constructor implementation was wrong. You assigned the parameters to themself but did not initialize the members. The member initialization list is a special place where you can use the same name for member and argument without shadowing.
Alternatively call the constructor with parameters.
Glad to see you are trying to improve your C++ skills. However, there are few things you can do to improve the code quality and style:
per[i]->getdata(); // Get the data from the user. will not work.
You have not defined getdata() with a virtual prefix.
Mark the overloaded methods in your child classes with an override suffix.
The same problem exists for putdata()
Virtual destructor is missing.
Is it valid to create a Person object? I think not. Therefore:
Constructor can be protected.
Person::getdata() can be a pure virtual function.
Person::z is a public member variable.
Member variables in child classes (Student and Professor) are also public.
Mentioned already by #463035818_is_not_a_number that Person *per[n]; is not portable C++.
As suggested by #JulienLopez, generally avoid raw pointers in C++11 and above. Use std::shared_ptr or std::unique_ptr.
I think you're missing the point of a constructor a bit here.
The idea is to have all the data ready to create your instance before hand, and then instanciate your class (either Professor or Student).
So your constructors are good, but you getdata member functions are not.
The easiest way would be for your getdata functions to become free functions (or static member functions, in our case, it's pretty much the same), and they gather the datas needed for construction and instanciate your class. (and a clearer name wouldn't hurt while we're at it)
Professor* createProfessorFromCin()
{
string a;int b,c;
cin>>a>>b>>c;
return new Professor(a,b,c,b+1);
}
and your calling code would just end up as
per[i] = createProfessorFromCin();
for example.
Also, few tips if you plan on improving this piece of code:
use smart pointers, it's not the 90's anymore, your code well be a lot more memory
safe (on the same vein, std::vector or std::array would be nice for the grades too)
If you plan on adding more subclasses of the sort, you should look into the Factory pattern for more OCP-friendly code.
Your Person class needs a virtual destructor (once you will fix your memory issues), otherwise your instances won't get destroyed properly.

C++ Passing an Object to Another Object From Another Class

I am trying to write this program using base classes and derived classes where there is a truck with a maximum capacity of 8 tons. Then this truck is loaded with 1.5 tons of apple and then loaded with 0.5 tons of kiwi. This would then diminish the amount of tons remaining to 6.5 when the apples are loaded and then to 6 when the kiwis are loaded. This is an assignment and in the main function I see they are calling the function loadCargo as follows truck.loadCargo(Apple()), I am unsure of what this is accomplishing. Please take a look at the complete code:
#include <iostream>
#include <string>
using namespace std;
class Cargo{
public:
double c = 0;
double getAmount(){
return c;
}
};
class Apple : public Cargo{
public:
double c = 1.5;
double getAmount(){
return c;
}
};
class Kiwi : public Cargo{
public:
double c = 0.5;
double getAmount(){
return c;
}
};
class Truck {
double maxCapacity = 8;
public:
void loadCargo(Cargo cargo){
maxCapacity = maxCapacity - cargo.getAmount();
}
void printInfo(){
cout << maxCapacity << " tons remaining" << endl;
}
};
int main() {
Truck truck;
truck.printInfo();
truck.loadCargo(Apple());
truck.printInfo();
truck.loadCargo(Kiwi());
truck.printInfo();
}
I thought that truck.loadCargo(Apple()) would pass an object of Apple to the object cargo. Therefore when loadCargo is called, it would access the getAmount function in Apple class and not in class Cargo but that is not happening. The output should be:
8 tons remaining
6.5 tons remaining
6 tons remaining
But currently it is the following since it just using the getAmount from the Cargo class:
8 tons remaining
8 tons remaining
8 tons remaining
EDIT: Since this is an assignment I cannot change anything in the main function or in the line that has void loadCargo(Cargo cargo).
First, you need to make your getAmount() function virtual:
class Cargo{
public:
double c = 0;
virtual double getAmount(){
return c;
}
};
Then, your derived classes will override the getAmount() function with their version.
Without the virtual keyword, if your function accepts a parameter of type Cargo, it will just used the Cargo::getAmount() function.
Second, you need to pass your object into by const-reference, like this:
class Truck {
double maxCapacity = 8;
public:
void loadCargo(const Cargo& cargo){
maxCapacity = maxCapacity - cargo.getAmount();
}
...
This will ensure that your cargo object inside the loadCargo function will refer to an Apple object or a Kiwi object. By passing by value, you're copying your Apples object into a Cargo object, and you fall over the slicing problem.
ETA: You would also need to change your getAmount() function to const like this:
// vvvvv
double getAmount() const {
return c;
}
Since you mention you cannot change your Truck class, you can do this by setting the value of c in your Cargo class constructor. Like this:
class Cargo{
public:
double c;
// Constructor, using an initializer list to set the value of `c`.
Cargo(const double c_value) :
c(c_value) {
}
double getAmount(){
return c;
}
};
Then, in your Apple and Kiwi classes, set the value of c inside the constructor, like this:
class Apple : public Cargo{
public:
// Set the Cargo's mass in the Apple constructor...
Apple() :
Cargo(1.5) {
}
// getAmount() function removed.
};
Finally, remove the getAmount() functions from your Apple and Kiwi classes (but KEEP for the Cargo class).
Final Note
Please bear in mind that passing Cargo by value will always suffer from the Slicing Problem (see link above) and should be avoided, though because your Apple and Kiwi objects don't have any member variables, it will still work for your assignment. If inheritance is how your instructor wanted this to work, then passing Cargo by value into loadCargo(Cargo cargo) is bad C++ practice. This doesn't impact your assignment, but if you wish to take C++ seriously, bear this in mind for the future!
When you make this call:
truck.loadCargo(Apple());
You are indeed passing an Apple to truck. However, the declaration of loadCargo takes a Cargo object:
void loadCargo(Cargo cargo)
This slices the Apple and now you simply have a Cargo object that you call getAmount on. This returns 0, and so the maxCapacity doesn't change.
There are various ways to fix this, but the simplest would be to make loadCargo a template, so that it can accept any type without first converting it to a Cargo object:
template<typename Fruit>
void loadCargo(Fruit cargo)
Here's a demo.
Note that for this implementation, so long as Apple and Kiwi have the same member functions as expected of a Cargo object, you can avoid inheriting from a base class entirely.

Union and Class

I have a lot of free time ( there are also the Xmas holidays incoming! ) so, I tried to merge my two passions, video games and c++.
It is not a real video game project, was just some console script using classes.
So, here it comes the problem;
#include <stdafx.h>
#include <iostream>
class m4a1 {
public:
int Damage = 12;
int Mag = 30;
};
class ak47 {
public:
int Damage = 14;
int Mag = 24;
};
union Gun_Union {
m4a1 M4A1_Union;
ak47 Ak47_Union;
};
class Player {
public:
Gun_Union Gun_Player;
int Health = 200;
};
template <typename Type>
void Shot(Type* Player_Obj) {
Player_Obj->Gun_Player->AK47_Union->Mag--;
Player_Obj->Health = Player_Obj->Health - Player_Obj->Gun_Player->AK47_Union->Damage;
}
int main() {
Player Player_Obj;
Player_Obj.Gun_Player;
Shot <Player> (&Player_Obj);
std::cout << Player_Obj.Health;
}
At the line 35 it gives me the error
Player::Player(void) trying to refer to an eliminate function.
I discovered that changing Gun_Union with any other type in the class Player (line 25) it doesn't give me an error.
I also ask if the command Player_Obj.Gun_Player = Ak47_Union is possible;
Sorry for bad English.
To answer your original question, your union has non-trivial members (because of the initializers), but has no constructor. You need to tell the compiler which of the union's members you want to have initially, because the compiler must construct one or the other. That can be done for example by giving the union a constructor.
And then you're mixing . and -> operators.
Here's a version that compiles (I don't dare call it "fixed" though):
#include <iostream>
class m4a1 {
public:
int Damage = 12;
int Mag = 30;
};
class ak47 {
public:
int Damage = 14;
int Mag = 24;
};
union Gun_Union {
m4a1 M4A1_Union;
ak47 Ak47_Union;
Gun_Union() : Ak47_Union() {} // <=== the initial state is ak47
};
class Player {
public:
union Gun_Union Gun_Player;
int Health = 200;
};
template <typename Type>
void Shot(Type* Player_Obj) {
Player_Obj->Gun_Player.Ak47_Union.Mag--;
Player_Obj->Health = Player_Obj->Health - Player_Obj->Gun_Player.Ak47_Union.Damage;
}
int main() {
Player Player_Obj;
Player_Obj.Gun_Player;
Shot <Player>(&Player_Obj);
std::cout << Player_Obj.Health;
}
I also ask if the command Player_Obj.Gun_Player = Ak47_Union is possible; Sorry for bad English.
So what you're trying to do here is set the player's current weapon to an ak47. Here's the problem. Your union represents a class, not just a regular type. This is a problem for a few reasons.
A class is useful because it provides a blueprint from which to create similar but ultimately different objects. Since all of your weapon classes have the same structure (damage and magsize), there's no reason to have separate classes for each weapon.
Furthermore, a class here is tricky because in order for you to use a class, you must first either instantiate it or declare it's member functions static. The problem here comes from the fact that you're using a union to handle the player weapon. A union won't work because unions by definition hold things (one at a time), which is not a thing here because we want a static class.
Now, you could instantiate your weapon classes and actually add them to the union, like so:
int main()
{
Player Player_Obj;
Player_Obj.Gun_Player;
// Remember to pick better names for classes, not the metasynctactic
// b.s. I'm using
m4a1 m4a1_class_instance;
ak47 ak47_class_instance;
// Populate the union
union Gun_Union weapons;
weapons.Ak47_Union = ak47_class_instance;
// Finally, set player weapon
Player_Obj.Gun_player = weapons;
// <Rest of program...>
As I'm sure you can tell, this is clunky and doesn't really make sense. Why are we istantiating those classes? It doesn't really make sense. I think a redesign here would be great, especially because you wouldn't have to deal with the union, which was tripping you up with regards to setting the player's weapon. Here is a pretty basic one.
// Using constants for simplicity
// Weapon Ids
const auto M4A1 = 1000;
const auto AK47 = 1001;
const auto FIRST_WEAPON_ID = M4A1;
const auto LAST_WEAPON_ID = AK47;
class Player {
int weapon_id;
int health;
int damage;
int magazine_capacity;
int magazine;
public:
void setWeapon(const int weapon_id) {
// Validate id first; you should somehow handle error,
// I'm just exiting the function here
if ((weapon_id < FIRST_WEAPON_ID) || (weapon_id > LAST_WEAPON_ID)) return;
switch (weapon_id) {
case M4A1: {
damage = 12;
magazine_capacity = 30;
} break;
case AK47: {
damage = 14;
magazine_capacity = 24;
} break;
default: // Error should have been handled in validation, but
// it's best practice to guard your code everywhere
return;
}
}
};
This wouldn't be the full class obviously, I only wrote one function just to show you how I would implement a more naive method, but the actually implementation details are up to you, I would really stress the design concepts, specifically the following:
Accessing Class Members
You don't want to access class members directly, especially when you're talking about a pointer. The problem of 'who owns the data pointed to by the pointer' is a significant one, and it's why we now have shared and unique pointers. Regardless of which pointer you use though, you should have something like <return-type> getValue() const. That way your data is protected by a layer of indirection. Likewise, to set a value you can do something like void setValue(const <type> value) and now you can do validation or whatever in that function.
Unions
First, I don't recommend using unions. In your particular case, using a union doesn't really make sense because you already have the pointer in the player class (before you edited your question), which means a player can already only hold one. Having a union doesn't add anything at that point. Additionally, using unions isn't recommended, mostly because you lose some type-safety. If you do want something union-like, you might want to check out std::variant in the STL.
Your Template Function
Generally speaking, template functions are for generic programming, where they can take in a type T and you don't have to worry about rewriting code unnecessarily. In your case, it shouldn't apply.
You mentioned you were just doing this for practice, so I understand a clean design wasn't your goal, but I think it's never too early to start thinking about design principles.

Array of Pointers in C++

I'm trying to writing some code for my c++ class. I'm using eclipse. I'm having a hard time trying to understand some of the instructions in the problem.
I've created a base class called Ship and then used inheritance for my CruiseShip class and CargoShip class.
For the CruiseShip class, I'm instructed to create
A print function that overrides the print function in the base class. The CruiseShip
class’s print function should display only the ship’s name and the maximum number
of passengers.
And similarly for the CargoShip class
A print function that overrides the print function in the base class. The CargoShip
class’s print function should display only the ship’s name and the ship’s cargo capacity.
I'm not sure what it means to "override" the print function in the base class.
It also instructs me to
Demonstrate the classes in a program that has an array of Ship pointers. The array
elements should be initialized with the addresses of dynamically allocated Ship ,
CruiseShip , and CargoShip objects. The program should then step through the array, calling
each object’s print function.
#include <iostream>
#include <string>
using namespace std;
class Ship
{
protected:
string ship_name;
int year_built;
public:
Ship()
{
ship_name="";
year_built=0;
}
void set_ship_name(string str)
{
ship_name=str;
}
void set_year(int y)
{
year_built=y;
}
int get_year()
{
return year_built;
}
string get_ship_name()
{
return ship_name;
}
void print(string, int)
{
cout<<"Ship name is "<<ship_name<<" and it was built in the year "<<year_built<<endl;
}
};
class CruiseShip: public Ship
{
private:
int max_passengers;
public:
CruiseShip()// :Ship(str,year)
{
max_passengers=0;
}
void set_passengers(int pass)
{
max_passengers=pass;
}
int get_passengers()
{
return max_passengers;
}
void print1(string, int)
{
cout<<"Ship name is "<<get_ship_name()<<" and max number of passengers are "<<max_passengers<<endl;
}
};
class CargoShip: public Ship
{
private:
int cargo_capacity_in_tons;
public:
CargoShip()//:Ship (str,year)
{
cargo_capacity_in_tons=0;
}
void set_capacity(int pass)
{
cargo_capacity_in_tons=pass;
}
int get_capacity()
{
return cargo_capacity_in_tons;
}
void print2(string, int)
{
cout<<"Ship name is "<<get_ship_name()<<" and its capacity is "<<cargo_capacity_in_tons<<" Tons."<<endl;
}
};
int main(){
CruiseShip ship1;
CargoShip ship2;
string ship_name1;
string ship_name2;
int year_built1;
int year_built2;
int max_passengers;
int cargo_capacity_in_tons;
cout<<"What is the name of the cruise ship?"<<endl;
cin>>ship_name1;
ship1.set_ship_name(ship_name1);
cout<<"What year was "<<ship_name1<<" built in?"<<endl;
cin>>year_built1;
ship1.set_year(year_built1);
cout<<"What is the maximum capacity of "<<ship_name1<<"?"<<endl;
cin>>max_passengers;
ship1.set_passengers(max_passengers);
//ship1.print(ship_name1, year_built1);
ship1.print1(ship_name1, max_passengers);
cout<<"What is the name of the cargo ship?"<<endl;
cin>>ship_name2;
ship2.set_ship_name(ship_name2);
cout<<"What year was "<<ship_name2<<" built in?"<<endl;
cin>>year_built2;
ship2.set_year(year_built2);
cout<<"What is the maximum capacity of "<<ship_name2<<" in tons?"<<endl;
cin>>cargo_capacity_in_tons;
ship2.set_capacity(cargo_capacity_in_tons);
ship2.print2(ship_name2, cargo_capacity_in_tons);
return 0;
}
Let´s say you have the following classes:
class Animal
{
private:
int x;
int y;
public:
virtual string sound() {return "Animal";}
void move() {x += 1; y+=1;}
};
class Cow
{
string sound() {return "Muh"} //this is overriding
string sound(string soundYouWant) {return soundYouWant;} //this is not overriding as string sound(string soundYouWant) is not the same as string sound()
void move() {x += 1; y+=1;} //this is also not overriding as move() in Animal has no virtual
};
So to summarize, overriding means you have a virtual method in the base class and you re-declare it in the derived class. This way, you are able to re-define it for every derived class (the method-body can be different for the base class and each of its derived classes).
Now to dynamic allocated arrays:
int size;
std::cin >> size;
int *array = new int[size]; //the array is stored on the heap
delete[] array; //deallocates the array and so frees the memory
If you create an array on the stack (without new), you either have to hardcode its size using literals (0, 1, 2, ...) or using a const int variableName. This way, the compiler knows the array size during compile time. So you have to know the array size while writing your program. Consequently, the compiler wouldn´t allow you to do this: std::cin >> size;.
Using new (dynamical arrays) you are allowed to specify the array size during compile time. So it is legal to let your program calculate the array size or take it as an user input. With dynamic arrays you also have a lot, lot, lot more memory than using the small stack (stackoverflow).
int *array: obviously the memory content is interpreted as integers. *array points to the first element of the array. int *array does NOT know the SIZE of the array. You have to keep track of that yourself.
new int[size]: You are reserving space for size * integers on the heap.
You might know that C++ does not have a garbage collector. This is when delete[] array; comes into play. When you don´t need array anymore (this includes other pointers pointing to array) you should call delete to free the memory. With small, short running programs, forgetting it won´t matter as the OS (operation system) will free the memory after your program has terminated. Nevertheless, you should use delete as not using it is very bad still and will lead to trouble with bigger programs. You should place delete in the destructor of a class (~clasname()) if you use array within a class.

C++ Object-oriented programming

I have 1 question because I am pretty curious how to handle with such problem.
I have base class called "Pracownik" (Worker) and 2 subclasses which are made from public Pracownik;
- Informatyk (Informatic)
- Księgowy (Accountant)
Writing classes is easy. Made them pretty fast but I have small problem with main because I am helping friend with program but I was not using C++ for a while. So:
This is my header file "funkcje.h"
#include <iostream>
using namespace std;
class Pracownik
{
private:
string nazwisko;
int pensja;
public:
Pracownik(string="",int=0);
~Pracownik();
string getNazwisko();
int getPensja();
friend double srednia_pensja(int,Pracownik);
};
class Informatyk : public Pracownik
{
private:
string certyfikat_Cisco;
string certyfikat_Microsoft;
public:
Informatyk(string="",int=0, string="", string="");
~Informatyk();
void info();
};
class Ksiegowy : public Pracownik
{
private:
bool audytor;
public:
Ksiegowy(string="",int=0, bool=false);
~Ksiegowy();
void info();
};
double srednia_pensja(int,Pracownik);
These are definitions of my functions "funkcje.cpp"
#include "funkcje.h"
Pracownik::Pracownik(string a,int b)
{
nazwisko=a;
pensja=b;
}
Pracownik::~Pracownik()
{
}
string Pracownik::getNazwisko()
{
return nazwisko;
}
int Pracownik::getPensja()
{
return pensja;
}
Informatyk::Informatyk(string a, int b, string c, string d) : Pracownik(a,b)
{
certyfikat_Cisco=c;
certyfikat_Microsoft=d;
}
Informatyk::~Informatyk()
{
}
Ksiegowy::Ksiegowy(string a, int b, bool c) : Pracownik(a,b)
{
audytor=c;
}
Ksiegowy::~Ksiegowy()
{
}
void Informatyk::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Certyfikat Cisco: "<<certyfikat_Cisco<<endl;
cout<<"Certyfikat Microsoft: "<<certyfikat_Microsoft<<endl;
}
void Ksiegowy::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Audytor: ";
if(audytor)
cout<<"Tak"<<endl;
else
cout<<"Nie"<<endl;
}
double srednia_pensja(int a,Pracownik *b)
{
return 0;
}
And finally main!
#include <iostream>
#include "funkcje.h"
using namespace std;
int main()
{
Pracownik lista[10];
Pracownik *lista_wsk = new Pracownik[10];
Informatyk a("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk b("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk c("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy d("Kowalski4",4000,1);
Ksiegowy e("Kowalski5",5000,0);
lista[0]=a;
lista[1]=b;
lista[2]=c;
lista[3]=d;
lista[4]=e;
Informatyk *ab = new Informatyk("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk *ac = new Informatyk("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk *ad = new Informatyk("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy *ae = new Ksiegowy("Kowalski4",3000,1);
Ksiegowy *af = new Ksiegowy("Kowalski5",3000,0);
lista_wsk[0]=*ab;
lista_wsk[1]=*ac;
lista_wsk[2]=*ad;
lista_wsk[3]=*ae;
lista_wsk[4]=*af;
for(int i;i<5;i++)
{
lista[i].info();
cout<<endl;
}
cout<<endl;
// for(int i;i<5;i++)
// {
// lista_wsk[i].info();
// }
return 0;
}
Ok and here goes my questions:
I had to create array which is filled with base class objects "Pracownik".
Secondary i had to create array which is full of pointers to class "Pracownik" objects.
(Hope those 2 first steps are done correctly)
Next thing I had to write to array 3 objects of class Informatic and 2 of class Accountant.
So I ve created 5 objects manually and added them into the array in such way array[0]=a;. I guess this is still good.
Next thing i had to create and add similar objects to array of pointers using new. So I ve created array with new and pointers to objects with new. (Hope thats correct 2).
And FINALLY:
I had to use info() on added to array objects.
This is my main question if my array is type "Pracownik" and I want to use function info() from subclasses how should I do that? And how compiler will know if he should use info() from Accountant or Informatic while I am trying to show those information using "for".
In an array of Pracownik, the elements are of type Pracownik. Any information about the objects being of a subclass of Pracownik are lost when you copy the elements into the array.
This is called object slicing and leads to the fact that there is no way to invoke Informatyk::info() on these objects.
If you want to call methods of a subclass, you have to prevent object slicing by storing pointers or references in the array.
As Oswald says in his answer,
Pracownik * lista_wsk = new Pracownik[10];
allocates an array of 10 Pracownik objects. This is probably not what you want. With polymorphism involved, we usually want to deal with pointers or references. Hence, you'd want an array of Pracownik * pointers. Since you already know at compile-time that it will have 10 members, there is no need for a dynamic allocation here. I think you've meant to write
Pracownik * lista_wsk[10];
instead. Now we don't put objects but pointers to objects into the array. For example:
lista_wsk[2] = new Informatyk("Kowalski3", 3000, "Cisco3", "Microsoft3");
And then we can iterate over the items like so:
for (unsigned i = 0; i < 10; ++i)
std::cout << lista_wsk[i]->getNazwisko() << std::endl;
As you have already discovered, it is impossible to call a subclass function member on a superclass object. It would be possible to figure out the actual type at run-time yourslf by means of a cast.
for (unsigned i = 0; i < 10; ++i)
if (Informatyk * info_ptr = dynamic_cast<Informatyk *>(lista_wsk[i]))
info_ptr->info();
dynamic_cast returns a pointer to the target class if this is possible or a nullptr (which evaluates to false, hence the conditional) otherwise. Note however that this is considered very poor style. It is better to use virtual functions. Therefore, add
virtual void
info()
{
// Do what is appropriate to do for a plain Pracownik.
// Maybe leave this function empty.
}
to the superclass and again to the subclass
virtual void
info() // override
{
// Do what is appropriate to do for an Informatyk.
}
The function in the subclass with the same signature is said to override the function inherited from the superclass. Since the function is marked as virtual, the compiler will generate additional code to figure out at run-time what version of the function to call.
If you are coding C++11, you can make the override explicit by placing the keyword override after its type as shown above (uncomment the override). I recommend you use this to avoid bugs that arise from accidental misspelling or other typos.