I'm running into a situation where I need to use virtual get and set functions to access members of derived classes. However, as pointed out in the answers of this topic (which is quite similar to my problem by the way), this is a bad practice and may mean that my classes need to be redesigned. The thing is... I'm new to C++ and OOP in general, so I'm not sure how exactly I can write my code in a different way.
If you have the patience, I'll explain my situation: I'm working on a code that can solve two different types of problem (say ProblemX and ProblemY). Depending on the user input, you can solve only problemX, only ProblemY, or both at the same time. If you want to solve only ProblemX, you need only a variable named "memberX". If you want to solve only ProblemY, you need only "memberY". If you want to solve both at the same time, you'll need both "memberX" and "memberY", but the problems will depend on each other, so you can't solve each problem individually. In short, the code would be something like this:
#include <vector>
class Problem
{
public:
double memberX;
double memberY;
};
class MainProblem
{
public:
std::vector<Problem*> problems;
};
int main()
{
bool solveProblemX = true; // This variable will depend on user input
bool solveProblemY = true; // This variable will depend on user input
MainProblem * mainProblem = new MainProblem();
for (int i = 0; i < 10000; i++)
{
Problem * problem = new Problem();
mainProblem->problems.push_back(*problem);
}
if (solveProblemX && solveProblemY)
{
// Solve interconnected problem, using "mainProblem->problem[i]->memberX" and "mainProblem->problem[i]->memberY";
}
else if (solveProblemX)
{
// Solve individual problem, using only "mainProblem->problem[i]->memberX";
}
else if (solveProblemY)
{
// Solve individual problem, using only "mainProblem->problem[i]->memberY";
}
else
{
// throw some exception
}
}
So far so good... the real problem is: although I gave only a small sample of the code, my real program is in fact very memory expensive (I actually have a large set of Problems, and each contains a large set of members). So, in order to reduce memory usage, I had to make some modifications. For example: If the user chooses to solve only problemX, the variable memberY will only be a dead weight, since the program won't need it. And vice versa.
Therefore, I thought in making derived classes for ProblemX and ProblemY individually, each containing their respectives data member. That way, I can assign to the vector of problems only a minor class containing specifically the data I need. And if I need both, I use another derived class named ProblemXandY, which containd both memberX and memberY. This is the code I thought:
#include <vector>
class Problem
{
public:
virtual double getMemberX(){ // Throw some exception};
virtual double getMemberY(){ // Throw some exception};
};
class ProblemX : public Problem
{
private:
double memberX;
public:
double getMemberX(){ return memberX; };
};
class ProblemY : public Problem
{
private:
double memberY;
public:
double getMemberY(){ return memberY; };
};
class ProblemXandY : public ProblemX, public ProblemY
{};
class MainProblem
{
public:
std::vector<Problem*> problems;
};
int main()
{
bool solveProblemX = true; // This variable will depend on user input
bool solveProblemY = true; // This variable will depend on user input
MainProblem * mainProblem = new MainProblem();
for (int i = 0; i < 10000; i++)
{
Problem * problem;
if (solveProblemX && solveProblemY)
problem = new ProblemXandY();
else if (solveProblemX)
problem = new ProblemX();
else if (solveProblemY)
problem = new ProblemY();
else
// throw some exception
mainProblem->problems.push_back(*problem);
}
if (solveProblemX && solveProblemY)
{
for (int i = 0; i < 10000; i++)
{
double memberX = mainProblem->problems[i]->getMemberX();
double memberY = mainProblem->problems[i]->getMemberY();
// Solve interconnected problem, using "memberX" and "memberY"
}
}
else if (solveProblemX)
{
for (int i = 0; i < 10000; i++)
{
double memberX = mainProblem->problems[i]->getMemberX();
// Solve individual problem, using only "memberX";
}
}
else if (solveProblemY)
{
for (int i = 0; i < 10000; i++)
{
double memberY = mainProblem->problems[i]->getMemberY();
// Solve individual problem, using only "memberY";
}
}
else
{
// throw some exception
}
}
My question is regarding the virtual get functions on the class Problem. It feels strange, I agree, since the variables memberX and memberY don't belong to the class Problem. However, without it I wouldn't be able to call mainProblem->problems[i]->getMemberX(), since mainProblem->problems[i] is an object of the base class Problem, instead of the derived classes. As I said, my program is memory expensive. That's why I chose to have a single vector of Problems in the class MainProblem, instead of multiple vectors, one for each derived class. Is there something wrong with my structure here? Should I really redesign it? If so, can you think of an alternative?
Related
I am trying to optimize the run time of my code and I was told that removing unnecessary virtual functions was the way to go. With that in mind I would still like to use inheritance to avoid unnecessary code bloat. I thought that if I simply redefined the functions I wanted and initialized different variable values I could get by with just downcasting to my derived class whenever I needed derived class specific behavior.
So I need a variable that identifies the type of class that I am dealing with so I can use a switch statement to downcast properly. I am using the following code to test this approach:
Classes.h
#pragma once
class A {
public:
int type;
static const int GetType() { return 0; }
A() : type(0) {}
};
class B : public A {
public:
int type;
static const int GetType() { return 1; }
B() : {type = 1}
};
Main.cpp
#include "Classes.h"
#include <iostream>
using std::cout;
using std::endl;
using std::getchar;
int main() {
A *a = new B();
cout << a->GetType() << endl;
cout << a->type;
getchar();
return 0;
}
I get the output expected: 0 1
Question 1: Is there a better way to store type so that I do not need to waste memory for each instance of the object created (like the static keyword would allow)?
Question 2: Would it be more effective to put the switch statement in the function to decide that it should do based on the type value, or switch statement -> downcast then use a derived class specific function.
Question 3: Is there a better way to handle this that I am entirely overlooking that does not use virtual functions? For Example, should I just create an entirely new class that has many of the same variables
Question 1: Is there a better way to store type so that I do not need to waste memory for each instance of the object created (like the static keyword would allow)?
There's the typeid() already enabled with RTTI, there's no need you implement that yourself in an error prone and unreliable way.
Question 2: Would it be more effective to put the switch statement in the function to decide that it should do based on the type value, or switch statement -> downcast then use a derived class specific function.
Certainly no! That's a heavy indicator of bad (sic!) class inheritance hierarchy design.
Question 3: Is there a better way to handle this that I am entirely overlooking that does not use virtual functions? For Example, should I just create an entirely new class that has many of the same variables
The typical way to realize polymorphism without usage of virtual functions is the CRTP (aka Static Polymorphism).
That's a widely used technique to avoid the overhead of virtual function tables when you don't really need them, and just want to adapt your specific needs (e.g. with small targets, where low memory overhead is crucial).
Given your example1, that would be something like this:
template<class Derived>
class A {
protected:
int InternalGetType() { return 0; }
public:
int GetType() { static_cast<Derived*>(this)->InternalGetType(); }
};
class B : public A<B> {
friend class A<B>;
protected:
int InternalGetType() { return 1; }
};
All binding will be done at compile time, and there's zero runtime overhead.
Also binding is safely guaranteed using the static_cast, that will throw compiler errors, if B doesn't actually inherits A<B>.
Note (almost disclaimer):
Don't use that pattern as a golden hammer! It has it's drawbacks also:
It's harder to provide abstract interfaces, and without prior type trait checks or concepts, you'll confuse your clients with hard to read compiler error messages at template instantiantion.
That's not applicable for plugin like architecture models, where you really want to have late binding, and modules loaded at runtime.
If you don't have really heavy restrictions regarding executable's code size and performance, it's not worth doing the extra work necessary. For most systems you can simply neglect the dispatch overhead done with virtual function defintions.
1)The semantics of GetType() isn't necessarily the best one, but well ...
Go ahead and use virtual functions, but make sure each of those functions is doing enough work that the overhead of an indirect call is insignificant. That shouldn't be very hard to do, a virtual call is pretty fast - it wouldn't be part of C++ if it wasn't.
Doing your own pointer casting is likely to be even slower, unless you can use that pointer a significant number of times.
To make this a little more concrete, here's some code:
class A {
public:
int type;
int buffer[1000000];
A() : type(0) {}
virtual void VirtualIncrease(int n) { buffer[n] += 1; }
void NonVirtualIncrease(int n) { buffer[n] += 1; }
virtual void IncreaseAll() { for i=0; i<1000000; ++i) buffer[i] += 1; }
};
class B : public A {
public:
B() : {type = 1}
virtual void VirtualIncrease(int n) { buffer[n] += 2; }
void NonVirtualIncrease(int n) { buffer[n] += 2; }
virtual void IncreaseAll() { for i=0; i<1000000; ++i) buffer[i] += 2; }
};
int main() {
A *a = new B();
// easy way with virtual
for (int i = 0; i < 1000000; ++i)
a->VirtualIncrease(i);
// hard way with switch
for (int i = 0; i < 1000000; ++i) {
switch(a->type) {
case 0:
a->NonVirtualIncrease(i);
break;
case 1:
static_cast<B*>(a)->NonVirtualIncrease(i);
break;
}
}
// fast way
a->IncreaseAll();
getchar();
return 0;
}
The code that switches using a type code is not only much harder to read, it's probably slower as well. Doing more work inside a virtual function ends up being both cleanest and fastest.
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.
I have really been struggling with a piece of code for a couple days. The error message i receive when i run my code is:
error: array initializer must be an initializer list
accountStore (int size = 0) : accts(size) { }
There seem to be others with similar problems here but unfortunately I am unable to apply their solutions (either don't work or not applicable).
What I am simply attempting to do is create a container class (array, can't use vectors) of a class 'prepaidAccount' but I am just unable to get the constructor portion of the container class 'storeAccount' to work. See code snippet below:
class prepaidAccount{
public:
//prepaidAccount ();
prepaidAccount(string newPhoneNum, float newAvailBal) : phoneNumber(newPhoneNum), availableBalance (newAvailBal){} //constructor
double addBalance(double howMuch) {
availableBalance = howMuch + availableBalance;
return availableBalance;
}
double payForCall(int callDuration, double tariff) {
callDuration = callDuration/60; //convert to minutes
double costOfCall = callDuration * tariff;
if (costOfCall > availableBalance) {
return -1;
}
else {
availableBalance = availableBalance - costOfCall;
return costOfCall;
}
}
void setAvailBal(int newAvailBal) {availableBalance = newAvailBal;}
float getAvailBal() {return availableBalance;}
void setPhoneNum(string newPhoneNum) {phoneNumber = newPhoneNum;}
string getPhoneNum() const {return phoneNumber;}
private:
string phoneNumber;
float availableBalance;
};
class accountStore { //made to store 100 prepaid accounts
public:
accountStore (int size = 0) : accts(size) { }
....
private:
prepaidAccount accts[100];
}
In main I simply call accountStore Account;
Any help is absolutely welcome. I very recently started learning c++ and about classes and constructors so please bear with me.
Thanks
You can't initialize an array with int like accountStore (int size = 0) : accts(size) {}.
prepaidAccount doesn't have a default constructor, you have to write member initialization list like,
accountStore (int size = 0) : accts{prepaidAccount(...), prepaidAccount(...), ...} { }
The array has 100 elements, it's not a practical solution here.
As a suggestion, think about std::vector, which has a constructor constructing with the spicified count of elements with specified value. Such as,
class accountStore {
public:
accountStore (int size = 0) : accts(size, prepaidAccount(...)) { }
....
private:
std::vector<prepaidAccount> accts;
};
Given that you have specified that you do not want to use a container such as std::vector but would like to specify the size at runtime, your only option would be to manually implement dynamic allocation yourself. Also given that you are wanting create 100 objects at a time, I would suggest making a function that can construct a temporary object according to your needs and then use this to initialise your dynamically allocated array. Consider the below code as a good starting point. (WARNING untested code.)
class prepaidAccount {
public:
// Constructor
prepaidAccount(string newPhoneNum, float newAvailBal)
: phoneNumber(newPhoneNum), availableBalance(newAvailBal) {}
// Default Constructor needed for dynamic allocation.
prepaidAccount() {}
/* your code*/
};
// Used to construct a tempoary prepaid account for copying to the array.
// Could use whatever constructor you see fit.
prepaidAccount MakePrepaidAccount(/*some parameters*/) {
/* Some code to generate account */
return some_var;
}
class accountStore {
public:
// Explicit constructor to avoid implicit type-casts.
explicit accountStore(const int &size = 0)
: accts(new prepaidAccount[size]) {
for (int i = 0; i < size; i++) {
// Will call defualt assignment function.
prepaidAccount[i] = MakePrepaidAccount(/*some parameters*/);
}
}
// Destructor
~accountStore() {
// Cleans up dynamically allocated memory.
delete[] prepaidAccount;
}
prepaidAccount *accts;
};
Edit: Amongst the c++ community it is often questionable when choosing to use dynamic allocation when there is such an excellent and comprehensive library of smart pointers. For example an std::vector would be perfect in this situation.
I have a class like this:
class Wall
{
private :
Quad faces[6];
};
I have the constructor like this :
Wall::Wall(Quad f[], const float &mass, Vector3 center)
I want to initialize faces to be f(or copy f to faces),Quad is struct that doesn't have a default constructor.
Now I solved the problem by using faces{f[0],f[1],f[2],f[3],f[4],f[5]} in the initializer list but this requires c++11 which I'm afraid some of my friends don't have it, and I need to pass my code to them.
There are many similar questions but all of them seem to not have solutions other than switching to vector or using some complicated code which I don't want, as you can understand from the classes' name, a Wall doesn't need a vector(it only has 6 faces so why a vector).
Is this really hopeless ? isn't there any way ?
PS
Whether in the constructor body or in the initializer list, it doesn't matter.
changing to dynamic arrays(Quad *) doesn't matter either but keeping with static arrays is preferable.
Several options. The easiest is probably to subclass Quad with something that has a default constructor:
class Wall {
public:
Wall(Quad f[], ...) {
for (int i = 0; i < 6; ++i) faces[i] = f[i];
}
private:
class MyQuad : public Quad {
MyQuad() : Quad(...) {}
}
MyQuad faces[6];
};
Another option is to use placement new - note that the code below doesn't work out of the box since it is not doing proper alignment/padding and dealing with some aliasing issues, which are left as an exercise to the reader. It should give you a starting point though.
class Wall {
public:
Wall(Quad f[], ...) {
for (int i = 0; i < 6; i++) {
// TODO: take padding into account
new (&faces_data + sizeof(Quad) * i) Quad(f[i]);
}
}
~Wall() {
for (int i = 0; i < 6; i++) {
face(i).~Quad();
}
}
Quad& face(int idx) {
// TODO: take padding into account
return (reinterpret_cast<Quad*>(faces_data))[idx];
}
private:
// TODO: force proper alignment and take padding into account
char faces_data[sizeof(Quad) * 6];
};
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.