Reputation: 1012
The simplest example of my question can be seen in the following code snippet:
class Handle : public IHandle_<Handle>{
public:
Handle(std::unique_ptr<Derived> aDerived)
: derived(std::move(aDerived)),
base(*aDerived) {};
std::unique_ptr<Derived> derived;
Base& base;
};
Here, you can see that the Handle
class is essentially a wrapper around Derived
. More importantly, I wish to expose the base class of Derived
, Base
, by way of a reference. The reason for this is that, ultimately, I wish for Handle
to look something like this:
class Handle : public IHandle_<Handle>{
private:
std::unique_ptr<Derived1> myD1;
std::unique_ptr<Derived2> myD2;
public:
Handle(std::unique_ptr<Derived1> aD1)
: myD1(std::move(aD1)),
base1(*aD1),
base2(*aD1){};
Handle(std::unique_ptr<Derived2> aD2)
: myD2(std::move(aD2)),
base1(*aD2),
base2(*aD2){};
Base1& base1;
Base2& base2;
};
The reason I wish for Handle
to work like this is that I am using it as a 'Component' in an 'entity component system', and I'd like for this particular component to be instantiatable from two different concrete implementations of the same two base classes. I mention this because an 'entity component system' design pattern by definition departs from traditional object-oriented programming practices: in other words, I know there are other ways of accomplishing what I am trying to do, however I wish to make it work in some variation of what I have listed here.
Question
Why does the simple Handle
example shown in my first snippet fail? It compiles fine but seg-faults when trying to access a method in Base
. If I change the order in which I instantiate the member variables of Handle
, I get some errors at compile time which I think could provide some hints but I do not really understand what is going on.
Here is a full working example of Handle
and the classes it depends on:
#include <memory>
#include <iostream>
class Base{
public:
Base(int ax) : x(ax){};
virtual ~Base() = 0;
virtual void setVal(float a) = 0;
virtual float getVal() = 0 ;
int x;
};
Base::~Base(){}
class Derived : public Base{
public:
Derived(int ax, int az)
: Base(ax), z(az){};
int z;
};
class Concrete : public Derived{
public:
Concrete(int ax, int aw, int av)
: Derived(ax, aw),
v(av){};
void setVal(float a) override{
myVal = a;
}
float getVal() override{
return myVal;
}
float myVal;
int v;
};
class IHandle{
public:
virtual ~IHandle() = 0;
};
IHandle::~IHandle(){}
template <class T>
class IHandle_ : public IHandle{
public:
virtual ~IHandle_() = 0;
};
template <class T>
IHandle_<T>::~IHandle_(){};
class Handle : public IHandle_<Handle>{
public:
Handle(std::unique_ptr<Derived> aDerived)
: derived(std::move(aDerived)),
base(*aDerived) {};
std::unique_ptr<Derived> derived;
Base& base;
};
int main(){
// These two pointers are owned by an EntityManager
std::unique_ptr<Derived> ptr(new Concrete(1, 2, 3));
// we can get a reference to an IHandle from the EntityManager
std::unique_ptr<IHandle> aHandle(new Handle(std::move(ptr)));
// We need, specifically, a `Handle` implementation of `IHandle`
Handle& handle = static_cast<Handle&>(*aHandle);
// seg fault on the following line
handle.base.setVal(10.0);
std::cout << "a = " << handle.base.getVal() << std::endl;
return 0;
}
Upvotes: 0
Views: 69
Reputation: 1062
The members in a C++ class are initialized in the order you declare them, so looking at the first snippet, the order of initialization of the members in the Handle class is:
That said, it means that in the constructor the line
derived(std::move(aDerived))
will transfer the internal resources of aDerived
to derived
, reasonably resetting the state of aDerived
. So as soon as your code reach the statement
base(*aDerived)
base
will reference an empty (depends on your move constructor implementation inside Base and Derived class) object that most likely will be deleted from memory after the call of the constructor itself.
So, I believe that any reference to base
you got in your code are pointing to a not allocated memory, giving the SEG_FAULT error.
SEG_FAULT most of the time refers to code that is using (in your case writing, see setval() ) an area of memory not (yet or anymore) allocated for the running process.
Hope this may help, Have a good night, Stefano
Upvotes: 2