Store Derived Class Objects in Base Class Variables

Store derived class objects in base class variables

What you are seeing is Object Slicing.

You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.

Solution:

You should store pointer to object of Base class in the vector:

vector<Base*> 

By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well.

Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.

How can I store objects of derived classes within a data structure from which can be accessed attributes which the base class does not have?

I managed to solve this by storing the objects in the vector as type base class pointer Item* in this case, and creating virtual functions to retrieve the desired attributes:

class Item {
public:
std::string name;
Item(std::string n) : name{n} {};
virtual int getDamage() {return 0;}
virtual int getArmour() {return armour;}
}

class Weapon : public Item {
public:
int damage;
Weapon(std::string n, int dam) : damage{dam}, Item(n) {};
virtual int getDamage() {return damage;}
}

class Armour : public Item {
public:
int armour;
Weapon(std::string n, int arm) : armour{arm}, Item(n) {};
virtual int getArmour() {return armour;}

}

Weapon sword("Longsword", "10");
Armour helmet("Iron Helmet", "5");

std::vector<Item*> inventory = {&sword, &helmet};

Store BASE-DERIVED class in same container

Usually the only reason you'd put them in the same container is to use them as the base type, making use of polymorphism to simply not care what the specific type is. This requires using virtual methods. For example:

struct Pet {
virtual ~Pet();
virtual void speak(std::ostream &) const = 0;
};

Pet::~Pet() {}

struct Cat : public Pet {
virtual void speak(std::ostream &) const override;
};

void Cat::speak(std::ostream & o) const {
o << "Meow!\n";
}

struct Dog : public Pet {
virtual void speak(std::ostream &) const override;
};

void Dog::speak(std::ostream & o) const {
o << "Woof!\n";
}

Now we have a virtual method (speak(std::ostream&)) which we can invoke via a pointer-to-Pet without knowing the actual type of pet:

std::vector<std::unique_ptr<Pet>> pets;
pets.emplace_back(std::make_unique<Cat>());
pets.emplace_back(std::make_unique<Dog>());

for (auto const & pet : pets) {
pet->speak(std::cout);
}

Displays:

Meow!
Woof!

If you can't treat all of the objects in the container as a base instance like this, there is little reason to have them in the same container to begin with -- what would be the point?

Storing derived objects in a vector by base class, and upon removal return them to derived objects

By the feedback provided in the comments, I would go with a solution like this:

class Card {
private:
CardKind kind_;
std::fucntion<void (Card &)> attack_;
std::function<void (Card &)> effectFunc_;

std::unordered_map<std::string, boost::any> properties;

Card(CardKind kind, std::function<void (Card &)> attackFunc,
std::function<void (Card &)> effectFunc);
};

Printing variables of different derived class objects inside a single vector

One of the possible solutions is using virtual functions like in the following example. Take a look for the Print() methods below...

class Item
{
public:
int id;
Item(int id) { this->id = id; }
virtual void Print(std::ostream& os) { os << id << " "; }
};

class ItemTypeA : public Item
{
public:
int a;
ItemTypeA(int a, int id) : Item(id) { this->a = a; }
void Print(std::ostream& os) override { Item::Print( os ); os << a << std::endl; }
};

class ItemTypeB : public Item
{
public:
int b;
ItemTypeB(int b, int id) : Item(id) { this->b = b; }
void Print(std::ostream& os) override { Item::Print( os ); os << b << std::endl; }
};

int main()
{
std::vector<std::shared_ptr<Item>> items;
items.push_back(std::make_unique<ItemTypeA>(2, 0));
items.push_back(std::make_unique<ItemTypeB>(3, 1));

for ( auto& el: items ) { el->Print(std::cout); }
}

Create a vector of base class objects and store derived class objects within

Firstly, if you want to use polymorphism you need to store pointers in your vector. As the vector is the sole owner of the employees something like std::vector<std::unique_ptr<Employee>> would be suitable.

Edit: I see you have updated the vector to use pointers. But you are storing a pointer to a local stack allocated object, e.g mEmp. This will not work, when the mEmp variable goes out-of-scope at the closing brace the object will be deleted and you will be left with a dangling pointer in your vector that points to a deleted object. Using this dangling pointer is undefined behaviour. You need to allocate the Manager on the heap using new. Then the object will not be deleted when the variable goes out-of-scope but you do need to remember to delete the object when you are done. Something like unique_ptr makes this easy.

Regarding your questions:

  1. Try to minimize explicit casting, especially downcasting. At the point that you store the Employee in the vector it will be implicitly upcast from the derived class to Employee but other than that there is not much need for casting.
  2. You have roughly the right idea when it comes to overriding methods, if you call the virtual printEmp method on an Employee pointer it will call the override in the derived class.

    If you are happy for the user input to be the responsibility of the Employee classes you could simply add a virtual method that initializes the employee using suitable input from the user. But I would be tempted to keep that separate from your domain objects. You need a switch statement on the user choice of employee type anyway so polymorphism doesn't gain you much there.

    If you really want to use polymorphism for the employee creation I would suggest using something like the Abstract Factory pattern.

Here is my suggestion anyway:

#include <vector>
#include <string>
#include <iostream>
#include <memory>

class Employee {
public:
Employee(std::string fName, std::string lName, int sal);
virtual ~Employee();

virtual void printEmp();

protected:
std::string m_fName;
std::string m_lName;
int m_sal;
};

class Manager : public Employee {
public:
Manager(std::string fName, std::string lName, int sal, int meets, int hols);
void printEmp() override;

protected:
int m_meets;
int m_hols;
};

Employee::Employee(std::string fName, std::string lName, int sal)
: m_fName(fName), m_lName(lName), m_sal(sal) {
}

Employee::~Employee() {
}

void Employee::printEmp(){
std::cout << "First Name: " << m_fName << "\n"
<< "Last Name: " << m_lName << "\n"
<< "Salary: " << m_sal << "\n";
}

Manager::Manager(std::string fName, std::string lName, int sal, int meets, int hols)
: Employee(fName, lName, sal), m_meets(meets), m_hols(hols){
}

void Manager::printEmp(){
Employee::printEmp();
std::cout << "Meets/Week: " << m_meets << "\n"
<< "Holidays/Year: " << m_hols << "\n";
}

std::unique_ptr<Manager> createManager() {
std::cout << "Enter First Name: ";
std::string fNameInp;
std::cin >> fNameInp;

std::cout << "Enter Last Name: ";
std::string lNameInp;
std::cin >> lNameInp;

std::cout << "Enter Salary: ";
int salInp;
std::cin >> salInp;

std::cout << "Number of meetings/week: ";
int meetsInp;
std::cin >> meetsInp;

std::cout << "Number of holidays/year: ";
int holsInp;
std::cin >> holsInp;
std::cout << "\n";

return std::make_unique<Manager>(fNameInp, lNameInp, salInp, meetsInp, holsInp);
}

std::unique_ptr<Employee> createEmployee() {
int input;
std::cout << "1) Add a Manager, 2) Add an Engg, 3) Add a Researcher\n";
std::cin >> input;
switch (input){
case 1:
return createManager();
default:
return nullptr;
}
}

int main() {
std::vector<std::unique_ptr<Employee>> dBVector;

std::cout << "Welcome to Employee Database, Enter an option to continue...\n";
std::cout << "1) Add an Employee"
<< ", 2) Delete an Employee"
<< ", 3) Save Database"
<< ", 4) Exit\n";
int input;
std::cin >> input;

switch (input){
case 1:
dBVector.push_back(createEmployee());
break;
default:
break; // Do nothing
}

dBVector.at(0)->printEmp();
}

Live demo

Using/storing derived member in derived class with base class that stores base member

You may use delegating constructor:

class Derived: public Base
{
public:

Derived(std::unique_ptr<DerivedMember> _dmember):
Derived(_dmember, _dmember.get())
{}

// public access functions here as before
private:
Derived(std::unique_ptr<DerivedMember>& _dmember, DerivedMember* ptr):
Base(std::move(_dmember)),
dmember(ptr)
{}
private:
// handy handle to the derived class so we don't need to downcast the base (or even access it!)
DerivedClass* dmember
};


Related Topics



Leave a reply



Submit