生成一个没有虚拟函数的接口?[英] Generating an interface without virtual functions?

本文是小编为大家收集整理的关于生成一个没有虚拟函数的接口?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在编码游戏引擎,我已经为对象设置了此类:

class SceneManager //controls everything in the "world" game
{
    public:
        void Add(SceneObject* object); //adds to the vector
    private:
        vector<SceneObject*> _worldObjects; //the vector that contains all of them
}

和我在游戏上工作的所有类都从centencobject继承:

class SceneObject
{
    public: 
        virtual void Draw() = 0;
}

class Image : public SceneObject
{ }
class Sprite : public SceneObject
{ }
class Model3D : public SceneObject
{ }

所以我知道我可以为矢量中的所有对象调用draw(). 但是我一直在努力进行优化,并且我正在尝试摆脱所有继承和虚拟功能,而是使用构图,因为它们不能嵌入方式,并且在每次对象上执行时似乎是一个主要的性能问题基础.

我正在寻找一些C ++技术,我可以用来将这些技术存储在我的矢量中,然后在其上调用draw(),并正确地绘制了与之相关的对象.这也适用于我用作虚拟的update()函数.

所以此代码:

void SceneManager::Add(SceneObject* object)
{
    _worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
    for(unsigned int i = 0; i < _worldObjects.size(); i++)
    {
        _worldObjects[i]->Draw(); //SceneObject's being called
    }
}

...将成为:

void SceneManager::Add(Image* image)
{
    SceneObject* object = new SceneObject();
    //link object to image somehow, tried to use it as a member of image
    _worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
    for(unsigned int i = 0; i < _worldObjects.size(); i++)
    {
        //_worldObjects[i]->
        //I need somehow to be able to get the pointer back to the original class
        //It can be an image, sprite, model3d, anything
    }
}

我认为如果我添加一个开关或删除虚拟的情况,我不会获得任何性能,所以我试图弄清楚是否有一种处理此操作的方法.

有什么想法?

推荐答案

您可以使用免费功能来建模对象的drawable方面:

#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"; } 
}

现在,要么使用三个单独的向量(这可能是最佳的,具体取决于跨集合之间的对象之间的订购关系?),或者考虑一个变体类型向量:

1.使用变体类型

#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);
    }
}

后者的概念的完整证明: coliru /strong>

#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);
}

输出

drawing image
drawing sprite
drawing model3D
drawing image

2.单独收藏

使用单独矢量的替代方案: 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);
}

请注意,输出是不同的(排序):

drawing image
drawing image
drawing sprite
drawing model3D

其他推荐答案

解决您的特定请愿书是其他人已经做过的一件事.

但是,我认为您应该退后一步,考虑整个图片.这是明智的步骤吗?虚拟功能的任何可能替代方法都会引入可维护性问题,即难以修改甚至了解代码.

问题是:这真的有必要吗?它真的会补偿吗?

虚拟函数涉及将两个指针而不是仅一个指针.是的,这是的确,它不会被隐藏.但是,我认为这不是一个真正的问题.我确实会专注于算法级优化,并在删除虚拟策略之前浪费所有其他方法.

考虑到至少一个解决方案涉及将虚拟函数转换为常规函数(不是成员函数),删除虚拟函数的众所周知的优势(即对象本身的类)与IF的链.

也就是说,这是您的电话.

其他推荐答案

由于您似乎具有固定的数字类型,因此似乎合理的方法是每种类型的一个向量并分别应用操作,每种类型的操作:处理一系列异质对象将等同于某种破坏它是否使用虚拟函数不是.将如何调用各个对象的框架放入函数模板中,将方便地处理通用性.

本文地址:https://www.itbaoku.cn/post/627551.html

问题描述

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 Add(SceneObject* object); //adds to the vector
    private:
        vector<SceneObject*> _worldObjects; //the vector that contains all of them
}

And all classes I work on the game inherit from SceneObject:

class SceneObject
{
    public: 
        virtual void Draw() = 0;
}

class Image : public SceneObject
{ }
class Sprite : public SceneObject
{ }
class Model3D : public SceneObject
{ }

So I know I can call Draw() for all objects in my vector. But I've been working on optimizations and I'm trying to get rid of all inheritance and virtual functions, and use composition instead, since they can't be inlined and seems to be a major performance issue when performed on a per-object basis.

I'm looking for some C++ technique that I can use to be able to store a bunch of SceneObjects in my vector, and then call Draw() on it and it properly draws the object related to it. This will also work for the Update() function I'm using as virtual.

So this code:

void SceneManager::Add(SceneObject* object)
{
    _worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
    for(unsigned int i = 0; i < _worldObjects.size(); i++)
    {
        _worldObjects[i]->Draw(); //SceneObject's being called
    }
}

...would become:

void SceneManager::Add(Image* image)
{
    SceneObject* object = new SceneObject();
    //link object to image somehow, tried to use it as a member of image
    _worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
    for(unsigned int i = 0; i < _worldObjects.size(); i++)
    {
        //_worldObjects[i]->
        //I need somehow to be able to get the pointer back to the original class
        //It can be an image, sprite, model3d, anything
    }
}

I don't think if I add a switch or if/elses and removing the virtual I'd gain any performance, so I'm trying to figure if there's a clean way to deal with this.

Any ideas?

推荐答案

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

其他推荐答案

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.

其他推荐答案

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.