1
class Base
{
public:
    virtual ~Base() {}
    virtual void Foo() = 0;
};

class FirstDerived: public Base
{
public:

    void Foo() { cout << "FirstDerived" << endl; }
};

class SecondDerived: public Base
{
public:

    void Foo() { cout << "SecondDerived" << endl; }
};

union PreallocatedStorage
{
    PreallocatedStorage() {}
    ~PreallocatedStorage() {}

    FirstDerived First;
    SecondDerived Second;
};

class ContainingObject
{
public:

    Base* GetObject()
    {
        if (!m_ptr)
        {
            // TODO: Make runtime decision on which implementation to instantiate.
            m_ptr = new(&m_storage) SecondDerived();
        }

        return m_ptr;
    }

    ~ContainingObject()
    {
        if (m_ptr)
        {
            m_ptr->~Base();
        }
    }

private:

    PreallocatedStorage m_storage;
    Base* m_ptr = nullptr;
};

int main()
{
    auto object = make_unique<ContainingObject>();

    // ...
    // Later, at a point where I don't want to make more heap allocations...
    // ...
    auto baseObject = object->GetObject();
    baseObject->Foo();

    return 0;
}

What I'm trying to achieve here:

  • I need to instantiate a class that has virtual methods.
  • At the point in time I know exactly which derived class to instantiate, I cannot make further heap allocations (this is just out of curiosity, so the exact reason why is not relevant).
  • Hence, I want to somehow pre-allocate enough space to hold any possible implementation, and then decide later what class I'm going to instantiate in it.

Is there anything standards-non-compliant/undefined behavior in the above code?

TripShock
  • 4,081
  • 5
  • 30
  • 39
  • This question is effectively equivalent to whether you can enumerate all derived classes that will exist, ever. If you can, then you can find out how much storage you need (and with which alignment). – Kerrek SB Aug 11 '16 at 01:24
  • A [question](http://programmers.stackexchange.com/questions/318223/static-memory-idiom) that I asked which is relevant (I ended up doing something similar to this in the end) – DarthRubik Aug 11 '16 at 01:30
  • "Is there anything standards-non-compliant/undefined behavior in the above code?" - So far in your current code, nope. – WhiZTiM Aug 11 '16 at 01:32
  • Typically you would provide storage through a `std::aligned_union`, rather than through *actual objects*, which are themselves constructed and then overwritten. – Kerrek SB Aug 11 '16 at 08:49
  • @KerrekSB That is correct, I'm making the assumption that I can enumerate all possible derivations, and that I'm willing the pay the memory cost for the largest possible implementation. – TripShock Aug 11 '16 at 15:04
  • @DarthRubik Thanks, that does seem relevant. Embedded environments are one possible place where something like this might be useful. – TripShock Aug 11 '16 at 15:05
  • @KerrekSB Thanks for the suggestion of `std::aligned_union`, that does seem more appropriate. However, I don't think what you said about the construction of the objects is correct, because even in my proposed solution, the constructors of the individual objects in the union does *not* get invoked. Which is I guess part of the reason why I'm required to provide an explicit constructor for the union; the default one is deleted. – TripShock Aug 11 '16 at 15:15
  • @TripShock: Ah yes, sorry, I hadn't noticed that you had a union there. Your union does start out with the first declared member initialized, though. – Kerrek SB Aug 11 '16 at 21:15
  • @KerrekSB In this case, the first member is not automatically initialized, because it seems the defaulted constructor of PreallocatedStorage is deleted, hence I have to provide an explicit one, that doesn't initialize anything. I don't understand all the rules around when the special members of a union are deleted. This answer has some info: http://stackoverflow.com/a/26572651/179895 – TripShock Aug 15 '16 at 15:36

1 Answers1

0

The code is correct. See the comments on the question for some interesting insights, particularly the use of std::aligned_union which can be used as a generic replacement for the PreallocatedStorage union above.

TripShock
  • 4,081
  • 5
  • 30
  • 39