Search code examples
c++variablespolymorphismdeclaration

Is It Not Possible To Declare A Variable of Abstract Type In C++?


I have the following switch statement in C++ where BaseType is an abstract class:

switch (foo)
{
    case 1:
    {
        DerivedType t = SomeDerivedType(); //Instantiate some derived type.
        FunctionThatTakesBaseType(t);   //Pass it to this function.
        break;
    }
    case 2:
    {
        SomeOtherDerivedType t = SomeOtherDerivedType(); //Instantiate some other dervied type.
        FunctionThatTakesBaseType(t)          //Pass it to the same function.
        break;
    }
    case 3:
    {
        ...
    }
}

If this were C#, I'd write this more succinctly by declaring a base type variable outside of the switch statement, defining it in the switch, and calling the function just once after the switch, like this:

BaseType t;  //Declared outside of scope of the switch so I can call the function once afterward.
switch (foo)
{
case 1:
    t = new SomeDerivedType();
    break;
case 2:
    t = new SomeOtherDerivedType();
    break;
case 3:
    ...
}
FunctionThatTakesBaseType(t);

Is there a way to do this in C++? How do you declare an abstract type variable in an outer scope and define it with a concrete type in an inner scope? Is there a way to write C++ code as briefly as the C# code? You can't do this:

Base t; //ERROR: In C++ this attempts to instantiate the abstract base class!

And it seems like you can't do this:

Base* t; //OK, but to assign the variable inside the switch do you have to allocate the 
         //object on the heap?

Solution

  • Yes, you do have to use a pointer and heap allocation.

    BaseType *t; 
    
    switch (foo)
    {
    case 1:
        t = new SomeDerivedType();
        break;
    case 2:
        t = new SomeOtherDerivedType();
        break;
    case 3:
        ...
    }
    

    You might also consider using a smart pointer. E.g.

    std::unique_ptr<BaseType> t; 
    
    switch (foo)
    {
    case 1:
        t = std::make_unique<SomeDerivedType>();
        break;
    case 2:
        t = std::make_unique<SomeOtherDerivedType>();
        break;
    case 3:
        ...
    }
    

    This is exactly what's happening in languages where all objects are allocated on the heap.

    Stack allocation relies on knowing the size of an object. A virtual base class doesn't indicate the size of the derived class. Consider:

    struct X {
        virtual std::string to_string() = 0;
    };
    
    struct Y : public X {
        std::string mem_var;
    
        Y(std::string m) : mem_var(m) { }
    
        std::string to_string() override {
            return mem_var;
        }
    };
    
    struct Z : public X {
        std::string to_string() override {
            return "Z";
        }
    }
    

    Do instances of Y and Z require the same amount of memory?