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!

Foi útil?

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 called clone, that is responsible to generate a copy of the instance it is called on. Then the constructor of BaseB might become

    BaseB (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 the BaseA derivation that was used to construct BaseB. You'd have to adjust BaseA * instanceA; to read boost::shared_ptr <BaseA> instanceA; Then BaseB'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 BaseBs constructor anyway, at least the total memory consumption would not rise, though)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top