This is my first C++ related programming question. I consider myself a beginner in programming even though I have dealt with C++ in some ways for about half a year. My question yields at an inheritance problem that I recently faced in my work.
I have a class NodeMaster
that should handle a container (right now a map
) of different child objects inherited from the same base class NBase
- accessing them, change data, ...
// Base class
class NBase
{
private:
int m_ID;
public:
NBase() { m_ID = 0; };
~NBase() {};
...
}
// Child class
class NSampler : NBase
{
private:
int m_ID;
std::string m_state;
public:
NSampler(std::string state) : {
m_ID = 1;
m_state = state;
}
...
}
The childs of NBase
are 'registered' into the map
via some arbitrary index:
#include "NBase.h"
class NodeMaster
{
private:
std::map<int, std::shared_ptr<sbr::NBase>> m_mpNodes;
public:
void register_node(int nIdx, std::shared_ptr<sbr::NBase> pNode)
{
this->m_mpNodes.insert(std::pair<int, std::shared_ptr<sbr::NBase>>(nIdx, pNode->clone()));
}
std::shared_ptr<sbr::NBase> get_node(int idx)
{
std::map<int, std::shared_ptr<sbr::NBase>>::iterator itr;
for (itr = this->m_mpNodes.begin(); itr != this->m_mpNodes.end(); ++itr) {
if (itr->first == idx) {
return itr->second;
}
}
}
I read about this problem on SO to imporve my solution step-by-step and also read about the issue of Object Slicing. Therefore I put the objects into a std::shared_ptr
to guarantee the 'polymorphic behaviour' throughout the process. So far everything is good and I can add the Child class objects to the container via
std::shared_ptr<sbr::NSampler> sampler(std::make_shared<sbr::NSampler>("SAMPLER_INIT"));
NodeMaster nodeMater;
nodeMaster.register_node(sampler->get_ID(), sampler);
Now what gives me headaches is how to properly access the elements of m_mpNodes
later in the code ... The nodeMaster
itself is passed to another function by reference pre_process_lin(nodeMaster, m_Links)
and to access the elements inside this function I expected to just write e.g. nodeMaster.get_node(1)
but that does not return the inherited object 'NSampler' but rather 'NBase' ... to retrieve 'NSampler' I had to use a dynamic_cast
for smart pointers:
nodeMaster.get_node(1); // NBase
...
std::shared_ptr<sbr::NSampler> test_sampler = std::dynamic_pointer_cast<sbr::NSampler>(nodeMaster.get_node(1)); // NSampler
To get this going I had to add a copy ctor to both the NBase
and the NSampler
(for using the covariance feature of C++) and considering that I deal with smart pointers I read this blog post to implement the copy ctor into the classes
// Base class
class NBase
{
private:
int m_ID;
public:
NBase() { m_ID = 0; };
~NBase() {};
public:
std::shared_ptr<NBase> clone() const
{
return std::shared_ptr<NBase>(this->clone_impl());
}
private:
virtual NBase* clone_impl() const = 0;
...
}
// Child class
class NSampler : NBase
{
private:
int m_ID;
std::string m_state;
public:
NSampler(std::string state) : {
m_ID = 1;
m_state = state;
}
public:
std::shared_ptr<NSampler> clone() const
{
return std::shared_ptr<NSampler>(this->clone_impl());
}
private:
virtual NSampler* clone_impl() const override
{
return new NSampler(*this);
}
...
}
With this setup I solve my problem of stacking together a bunch of inherited class objects into some container (map
), but since this specific copy-ctor is called (in order to be able to add them to the container) the initial member variables (e.g. m_state
) get overwritten after 're-calling' the objects from the map
.
Is there a better way to wrap the process of inheritance, packing to container and unpacking without losing object data on the run due to the copy-ctor above?