How to allocate memory for an object I don't know the real type of? C++
-
29-09-2019 - |
Pergunta
I want to store an object of some derived class as a property of another object.
The problem for me is to know the size of the object to be stored, as it can be any derived class of a given base class. I cannot just allocate memory for the base class' object, right, as it's smaller. I suppose, typeof operator would also just give the size of the base class, right?
Here is my sample code. Please, see the comments to understand what I mean. Excuse me for the unoriginality…
BaseA {};
DerivedA1 : BaseA {public: void property() { cout << 1; }};
DerivedA2 : BaseA {public: void property() { cout << 2; }};
// etc. - several derived classes.
BaseB // Contains (or links to) an instance of class derived from BaseA.
{
public:
BaseA * instanceA;
BaseB (BaseA instanceAX)
{
// ??? => How to allocate memory for the object
// I don't know the real type of?
instanceA = new BaseA (instanceAX);
}
BaseB (BaseA instanceAX) { delete instanceA; }
};
main()
{
DerivedA1 instanceA1;
BaseB instanceB (instanceA1);
// Use "property" regardless of which derived class it belongs to.
instanceB.instanceA->property();
}
I could store a pointer instead of the object itself, it would be easier. But I'm not sure I want to rely on the caller to retain the property object for the life of instance instanceB.
Thanks!
Solução
Well, first of all, since you pass in your BaseA by value, and not by reference or pointer, you DO know the type; it's BaseA. BaseB was copied into a BaseA and that BaseA copy is sent to the function. Any extra data in BaseB is not contained in instanceAX. This is called "slicing".
But, to answer your question, you simply do not do it this way. You need a clone() function in your BaseB class if it is meant to work this way. Alternatively you might design a smart pointer or other object that can clone the instance for you. If you search for my name on comp.lang.c++ along with clone() you might come across a discussion I had there about this latter idea.
The clone function is of course a virtual function that returns a copy of the current object:
struct BaseA
{
virtual BaseA* clone() const = 0;
};
struct BaseB : BaseA
{
BaseB* clone() const { return new BaseB(*this); }
};
Outras dicas
I see two possibilities.
Give
BaseA
a purely virtual member function, maybe calledclone
, that is responsible to generate a copy of the instance it is called on. Then the constructor ofBaseB
might becomeBaseB (BaseA const &instanceAX) { instanceA = instanceAX.clone (); }
You could also just avoid passing raw pointers around and use
boost::shared_ptr
instead. This way, since it is reference-counted, your caller can't cause a premature destruction of theBaseA
derivation that was used to constructBaseB
. You'd have to adjustBaseA * instanceA;
to readboost::shared_ptr <BaseA> instanceA;
ThenBaseB
's constructor might look like:BaseB (boost::shared_ptr <BaseA> a) : instanceA (a) { }
The problem of the first aproach is that some client programmer that inherits from, say DerivedA1
may forget to implement clone
in his derivation. The principal problem of the second aproach would be to require every user of BaseB
to allocate the property object on the heap (Since you are making a copy inside BaseB
s constructor anyway, at least the total memory consumption would not rise, though)