c++polymorphismcompositiondynamic-dispatchrun-time-polymorphism

Can late-binding in C++ be done without the need for heap-memory when using composition?


late-binding or runtime-polymorphism needs 2 things: a base pointer and a virtual method.

class FrameWorkClassBase {
  public:
    virtual void method() = 0;
};

class FrameWorkClassDerived: public FrameWorkClassBase {
public:
    virtual void method() override { ... }
}

class ProductionConsumerClass { // uses composition
  public:
    void method() {
        this->m_sptr->method(); // dynamic dispatch
    }
  
  private:
    // ptr to base class, composition at work
    std::shared_ptr<FrameWorkClassBase> m_sptr =
                              std::make_shared<FrameWorkClassDerived>();
}

there seems to be a bias towards using heap memory for polymorphic object creation rather than stack allocated objects, especially when it comes to using "polymorphic object" as component objects. Is this a requirement: is it the nature of the C++ language as a whole that forces the use of heap memory for "polymorphic component objects" ? Are there any design patterns that overcome the need for using heap memory for "polymorphic component objects" ?

Note: I do not want to use base class l-value reference(FrameWorkClassBase&) as a data member in ProductionConsumerClass as i do not have life-time-guarantee/ownership of the referred object.


Solution

  • There is no need to use heap allocation for late binding to work:

    #include <iostream>
    
    struct Base {
      virtual void print() { std::cout << "Base\n"; }
    };
    
    struct Derived : public Base {
      void print() override { std::cout << "Derived\n"; }
    };
    
    struct Composed {
      Base b;
      Derived d;
    };
    
    int main() {
      Base b;
      Derived d;
      Composed c;
    
      Base &bb{b}, &bd{d}, &cbb{c.b}, &cbd{c.d};
    
      bb.print();   // Base
      bd.print();   // Derived
      cbb.print();  // Base
      cbd.print();  // Derived
    }
    

    In the example above, no heap allocation takes place and all reference variables are of type Base&. Late binding works just fine.