Generating an interface without virtual functions?

后端 未结 3 1022
迷失自我
迷失自我 2020-11-28 14:13

I\'m coding a game engine and I have this class set up for objects:

class SceneManager //controls everything in the \"world\" game
{
    public:
        void         


        
相关标签:
3条回答
  • 2020-11-28 14:42

    Solving your specific petition is one thing that others have already done.

    However, I think you should take a step back and consider the whole picture. Is this a wise step to take? Any possible alternative to virtual functions will introduce maintainability problems, i.e., difficulty to modify and even to understand code.

    The question is: is this really necessary? Will it really compensate?

    Virtual functions involve derreferencing two pointers instead of only one. And yes, it is true it won't be inlined. I don't think, however, this being a real issue. I would indeed concentrate in algorithm-level optimization, and waste all other approaches before removing virtual funcions.

    Take into account that at least one solution involves converting virtual functions to regular functions (not member functions), removing the well-known advantage of a virtual function (i.e., the class of the object itself) vs. a chain of if's.

    That's said, it is your call.

    0 讨论(0)
  • 2020-11-28 14:46

    Since you seem to have a fixed number types, it seems a reasonable approach would be the use of one vector per type and applying the operations separately for each type: processing a sequence of heterogeneous objects will amount to some disruption whether it is using virtual functions are not. Putting the framework of how the respective objects are called into a function template will conveniently deal with the commonality.

    0 讨论(0)
  • 2020-11-28 14:53

    You can use free functions to model the drawable aspect of your objects:

    #include <iostream>
    
    class Image { };
    class Sprite { };
    class Model3D { };
    
    namespace draw_aspect
    {
        void draw(Image const& image)     { std::cout << "drawing image\n";   } 
        void draw(Sprite const& sprite)   { std::cout << "drawing sprite\n";  } 
        void draw(Model3D const& model3D) { std::cout << "drawing model3D\n"; } 
    }
    

    Now, either use three separate vectors (this could well be most optimal, depending on the ordering relationship between the objects across collections?), or consider a variant type vector:

    1. Using variant types

    #include <boost/variant.hpp>
    using SceneObject = boost::variant<Image, Sprite, Model3D>;
    
    namespace draw_aspect {    
    
        struct draw_visitor : boost::static_visitor<> {
            template <typename T> void operator()(T const& t) const { draw(t); }
        };
    
        void draw(SceneObject const& sobj) { 
            static const draw_visitor _vis;
            boost::apply_visitor(_vis, sobj);
        }
    }
    

    A complete proof of concept of the latter: Live on Coliru

    #include <vector>
    
    class SceneManager //controls everything in the "world" game
    {
        public:
            void Add(SceneObject v) { _worldObjects.emplace_back(std::move(v)); }
            friend void draw(SceneManager const& sm) { return sm.draw(); }
        private:
            void draw() const {
                for(auto& sobj : _worldObjects)
                    draw_aspect::draw(sobj);
            } 
            std::vector<SceneObject> _worldObjects; //the vector that contains all of them
    };
    
    int main()
    {
        SceneManager sman;
    
        sman.Add(Image());
        sman.Add(Sprite());
        sman.Add(Model3D());
        sman.Add(Image());
    
        draw(sman);
    }
    

    Outputs

    drawing image
    drawing sprite
    drawing model3D
    drawing image
    

    2. Separate collections

    The alternative using separate vectors: Live on Coliru

    class SceneManager //controls everything in the "world" game
    {
        public:
            void Add(Image v)   { _images  .emplace_back(std::move(v)); }
            void Add(Sprite v)  { _sprites .emplace_back(std::move(v)); }
            void Add(Model3D v) { _model3Ds.emplace_back(std::move(v)); }
    
            friend void draw(SceneManager const& sm) { return sm.draw(); }
        private:
            void draw() const {
                for(auto& sobj : _images)   draw_aspect::draw(sobj);
                for(auto& sobj : _sprites)  draw_aspect::draw(sobj);
                for(auto& sobj : _model3Ds) draw_aspect::draw(sobj);
            } 
            std::vector<Image> _images;
            std::vector<Sprite> _sprites;
            std::vector<Model3D> _model3Ds;
    };
    
    int main()
    {
        SceneManager sman;
    
        sman.Add(Image());
        sman.Add(Sprite());
        sman.Add(Model3D());
        sman.Add(Image());
    
        draw(sman);
    }
    

    Note that the output is different (ordering):

    drawing image
    drawing image
    drawing sprite
    drawing model3D
    
    0 讨论(0)
提交回复
热议问题