How can I create a polymorphic object on the stack?

前端 未结 12 1672
谎友^
谎友^ 2021-01-12 05:40

How do I allocate a polymorphic object on the stack? I\'m trying to do something similar to (trying to avoid heap allocation with new)?:

A* a = NULL;

switch         


        
12条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-12 06:32

    I wrote a generic template to do it. Full code available here (it became too elaborate for the scope of this question). StackVariant object contains a buffer of the size of the biggest type out of the provided types, and biggest alignment as well. The Object is constructed on the stack using a 'placement new' and operator->() is used for polymorphic access to suggest the indirection. Also, it is important to make sure that if a virtual detor is defined, it should be called upon destruction of the object on the stack, so the template detor is doing just that using a SFINAE definition.

    (see usage example and output below):

    //  compile: g++ file.cpp -std=c++11
    #include 
    #include 
    
    // union_size()/union_align() implementation in gist link above
    
    template
    class StackVariant {
        alignas(union_align()) char storage[union_size()];
    public:
        inline Tbaseclass* operator->() { return ((Tbaseclass*)storage); }
        template
        StackVariant& init(TCtor_params&&...fargs)
        {
            new (storage) C(std::forward(fargs)...);      // "placement new"
            return *this;
        };
    
    
        template
        typename std::enable_if::value, void>::type
        call_dtor(){
            ((X*)storage)->~X();
        }
    
        template
        typename std::enable_if::value, void>::type
        call_dtor() {};
    
        ~StackVariant() {
            call_dtor();
        }
    };
    

    Usage example:

    #include 
    #include 
    #include "StackVariant.h"
    
    class Animal{
    public:
        virtual void makeSound() = 0;
        virtual std::string name() = 0;
        virtual ~Animal() = default;
    };
    
    class Dog : public Animal{
    public:
        void makeSound() final { std::cout << "woff" << std::endl; };
        std::string name() final { return "dog"; };
        Dog(){};
        ~Dog() {std::cout << "woff bye!" << std::endl;}
    };
    
    class Cat : public Animal{
        std::string catname;
    public:
        Cat() : catname("gonzo") {};
        Cat(const std::string& _name) : catname(_name) {};
        void makeSound() final { std::cout << "meow" << std::endl; };
        std::string name() final { return catname; };
    };
    
    using StackAnimal = StackVariant;
    
    int main() {
        StackAnimal a1;
        StackAnimal a2;
        a1.init("gonzo2");
        a2.init();  
        a1->makeSound();
        a2->makeSound();
        return 0;
    }
    //  Output:
    //  meow
    //  woff
    //  woff bye!
    

    Few things to note:

    1. I wrote it while trying to avoid heap allocations in performance critical functions and it did the job - 50% speed gains.
    2. I wrote it to utilize C++'s own polymorphic mechanisms. Before that my code was full of switch-cases like the previous suggestions here.

提交回复
热议问题