1

Imagine I have a Base class, and two classes that derive from it, One and Two. In Java, I could have the following scenario:

Base b;
if(condition)
  b = new One();
else
  b = new Two();   

where the object type is determined at runtime (the above objects go on the heap).

In C++, I want to be able to instantiate the object type at runtime as well - where all I know is that they both share the same Base type - but I want to keep it stack allocated, like so:

Base b;

What's the best way to do this?

nhnl
  • 13
  • 2
  • 1
    If you want your object to be allocated on stack - there is none (Java doesn't allocate its objects on the stack, so those 2 examples are not the same). Even if you could, your object would be [sliced](http://stackoverflow.com/questions/274626/what-is-object-slicing) to only `Base` type. – Algirdas Preidžius Nov 10 '16 at 15:08
  • 1
    You can't (in any advised maintainable way). It's stack allocated so its size must be known at allocation time. Remember in Java your object isn't stack allocated, so while the Java code written in C++ implies stack allocated, it doesn't imply that in Java. – djgandy Nov 10 '16 at 15:10
  • This is classic XY problem - http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem – Slava Nov 10 '16 at 15:12
  • @Slava I don't see how it is. The examples are there for clarification, but the question is still pretty general. – nhnl Nov 10 '16 at 15:21
  • @nhnl You are trying to implement something using this construction. You simply do design in java and try to implement in C++. This is wrong way. Things are done differently in C++. – Slava Nov 10 '16 at 15:24
  • @AlgirdasPreidžius Ah, I wasn't aware of that concept. Based on my understanding, Java will not automatically slice, but C++ will? Thank you – nhnl Nov 10 '16 at 15:30
  • @Slava Maybe I should have phrased the question more generally? Depending on program execution, I either want to instantiate an object of one derived type or another, and I only want it to live as long as the current stack. Again, this seems pretty general... can you direct to me towards how things should be done? – nhnl Nov 10 '16 at 15:35
  • @nhnl As explained in other comments - size of objects defined on stack, needs to be known at compile time, and since different types of classes, may have different sizes, it is always sliced to the type of the object you defined on stack - `base`. In Java, only primitive types are stored on stack, and objects are **never** stored on stack, so this problem doesn't exist there. – Algirdas Preidžius Nov 10 '16 at 15:36
  • @AlgirdasPreidžius Great explanation, thanks again – nhnl Nov 10 '16 at 15:38
  • @nhnl my answer below explains how to automatically limit the lifetime with the current stack frame. – davmac Nov 10 '16 at 15:38

4 Answers4

1

What's the best way to do this?

You can't. If you declare a variable type as Base, the stack allocation for it will be suitable for holding an instance of Base but not an instance of a derived type (which might be larger, though even if it is not, you still cannot do what you ask; the runtime type of a variable in C++ is always the same as its declared type). At best, you could slice the derived instance into a Base-type variable.

The best bet is to use a pointer, optionally wrapped in a shared_ptr or unique_ptr to give you similar semantics (i.e. to have the object be automatically destroyed when it goes out of scope, assuming ownership hasn't been transferred).

Base* b = (condition) ? (Base *) new One() : new Two();
auto bptr = shared_ptr<Base>(b);

Note that what this gives you is effectively the same as the Java. The object itself is heap allocated, but the reference to it is stack allocated. Despite the syntax, a reference-type Java variable is essentially equivalent to a pointer in C++.

Community
  • 1
  • 1
davmac
  • 20,150
  • 1
  • 40
  • 68
0

Take, for instance:

Derived d;
Base* b = &d;

d is on the stack (automatic memory), but polymorphism will still work on b.

If you don't have a base class pointer or reference to a derived class, polymorphism doesn't work because you no longer have a derived class. Take

Base c = Derived();

The c object isn't a Derived, but a Base, because of slicing. So, technically, polymorphism still works, it's just that you no longer have a Derived object to talk about.

Now take

Base* c = new Derived();

c just points to some place in memory, and you don't really care whether that's actually a Base or a Derived, but the call to a virtual method will be resolved dynamically.

So, it seems, unlike Java, there is no way to achieve dynamic binding without heap allocation or pointer way.

Kaidul
  • 15,409
  • 15
  • 81
  • 150
0

As comments said you cannot. Well, you could but that wouldn't run:

Base &b = *(condition ? (Base *)&One() : (Base *)&Two()); // BROKEN CODE DO NOT USE

(Builds using -fpermissive, but don't do this!)

since One & Two objects are temporary objects. b would be invalid as soon as you step through next line => don't do this (not sure I said it already)

The following works, but not perfect: build both objects on the stack, so OK only if you can afford it. Just choose according to condition:

One one;
Two two;
Base &b = *(condition ? (Base *)&one : (Base *)&two);

To avoid the double allocation, the closest thing I can think of in terms of usage would be to take a reference of allocated objects (still not auto variables, sorry):

Base &b = *(condition ? (Base *)new One() : (Base *)new Two());

So you can use b as Base in your code.

You still have to delete memory with

delete (&b);
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
-1

In order to use inheritance in C++ you will need to define a pointer, not a static object, and then instantiate it with new keyword.

Example:

Base* b;
if(condition)
  b = new One();
else
  b = new Two(); 
kejn
  • 112
  • 6