为特定类型的所有应用程序创建基类是好的设计吗?

发布于 2024-08-20 03:36:59 字数 2264 浏览 4 评论 0原文

我正在尝试用 C++ 编写图形应用程序。它目前使用 OGRE 进行显示,但我希望它能够与 Irrlicht 或任何其他引擎配合使用,甚至是支持我的需求的自定义渲染引擎。这是一个相当长的问题,所以我希望获得有关重新标记/清理的帮助(如果需要)。我将从一些背景开始。

该应用程序具有三个主要状态:
1.显示光栅化场景
2. 显示同一场景的光线追踪版本
3. 显示场景的混合版本

显然,我可以将我的应用程序分为四个主要部分:
1. 一个状态管理系统,用于在上述模式之间切换。
2. 一个可以接收键盘和鼠标输入的输入系统。
3. 用于显示的光栅引擎。
4.光线追踪系统。

任何包含上述内容的应用程序都需要能够:
1. 创建一个窗口。
2. 执行允许在该窗口中进行渲染所需的所有步骤。
3. 初始化输入系统。
4. 初始化状态管理器。
5. 开始循环(和渲染!)。

我希望能够随时更改渲染引擎/状态管理器/输入系统/光线追踪系统,只要满足某些最低要求即可。恕我直言,这需要将接口与实现分开。考虑到这一点,我为上述系统创建了界面。

那时,我注意到该应用程序也有一个通用的“界面”。所以我想用虚拟方法将其抽象为一个 ApplicationBase 类。一个特定的应用程序,例如使用 OGRE 进行窗口创建、渲染等的应用程序将从此类派生并实现它。

我的第一个问题是 - 这样的设计是个好主意吗?

下面是基类的代码:

#ifndef APPLICATION_H  
#define APPLICATION_H  
namespace Hybrid
{

    //Forward declarations
        class StateManager;
    class InputSystem;

    //Base Class for all my apps using hybrid rendering.
    class Application
    {
        private:
            StateManager* state_manager;
            InputSystem* input_system;
        public:
            Application()
            {
                try
                {
                    //Create the state manager
                    initialise_state_manager();
                    //Create the input system
                    initialise_input_system();
                }
                catch(...) //Change this later
                {

                    //Throw another exception
                }

            }   

            ~Application()
            {           
                delete state_manager;
                delete input_system;
            }

            //If one of these fails, it throws an 
            //exception.
            virtual void initialise_state_manager() = 0;
            virtual void initialise_input_system() = 0;
            virtual void create_window() = 0;
            //Other methods.

    };

#endif

当我使用 OGRE 时,我依靠 OGRE 来创建窗口。这需要在我的派生类中调用 createWindow() 函数之前初始化 OGRE。当然,事实上,createWindow 将首先被调用!这给我留下了以下选择:
1. 将基类构造函数留空。
2. 在派生类实现中,将初始化 OGRE 作为 createWindow 函数的一部分。
3. 在我的基类中添加一个初始化渲染系统纯虚函数。这存在在派生类中强制实现虚拟实现的风险,而派生类对此类方法没有用处。

我的第二个问题是 - 对于选择这些策略之一来初始化 OGRE,您有何建议?

I am trying to write a graphics application in C++. It currently uses OGRE for display, but I'd like it to work with Irrlicht or any other engine, even a custom rendering engine which supports my needs. This is a rather long question, so I'd appreciate help on re-tagging/ cleanup (if necessary). I'll start with a little background.

The application has three major states:
1. Display rasterized scene
2. Display a ray traced version of the same scene
3. Display a hybrid version of the scene

Clearly, I can divide my application into four major parts:
1. A state management system to switch between the above modes.
2. An input system that can receive both keyboard and mouse input.
3. The raster engine used for display.
4. The ray tracing system.

Any application encompassing the above needs to be able to:
1. Create a window.
2. Do all the steps needed to allow rendering in that window.
3. Initialize the input system.
4. Initialize the state manager.
5. Start looping (and rendering!).

I want to be able to change the rendering engine/state manager/input system/ ray tracing system at any time, so long as certain minimum requirements are met. Imho, this requires separating the interface from the implementation. With that in mind, I created the interfaces for the above systems.

At that point, I noticed that the application has a common 'interface' as well. So I thought to abstract it out into an ApplicationBase class with virtual methods. A specific application, such as one which uses OGRE for window creation, rendering etc would derive from this class and implement it.

My first question is - is it a good idea to design like this?

Here is the code for the base class:

#ifndef APPLICATION_H  
#define APPLICATION_H  
namespace Hybrid
{

    //Forward declarations
        class StateManager;
    class InputSystem;

    //Base Class for all my apps using hybrid rendering.
    class Application
    {
        private:
            StateManager* state_manager;
            InputSystem* input_system;
        public:
            Application()
            {
                try
                {
                    //Create the state manager
                    initialise_state_manager();
                    //Create the input system
                    initialise_input_system();
                }
                catch(...) //Change this later
                {

                    //Throw another exception
                }

            }   

            ~Application()
            {           
                delete state_manager;
                delete input_system;
            }

            //If one of these fails, it throws an 
            //exception.
            virtual void initialise_state_manager() = 0;
            virtual void initialise_input_system() = 0;
            virtual void create_window() = 0;
            //Other methods.

    };

#endif

When I use OGRE, I rely on OGRE to create the window. This requires OGRE to be initialised before the createWindow() function is called in my derived class. Of course, as it is, createWindow is going to be called first! That leaves me with the following options:
1. Leave the base class constructor empty.
2. In the derived class implementation, make initialising OGRE part of the createWindow function.
3. Add an initialize render system pure virtual function to my base class. This runs the risk of forcing a dummy implementation in derived classes which have no use for such a method.

My second question is- what are your recommendations on the choice of one of these strategies for initialising OGRE?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

随波逐流 2024-08-27 03:36:59

您在一个类中混合了两个不相关的函数。首先,它作为声明和初始化 StateManager 和 InputSystem 成员的语法快捷方式。其次,它声明了抽象create_window函数。

如果您认为应该有一个通用接口 - 编写一个接口(纯抽象类)。

此外,编写类似 OgreManager 自包含类的内容,其中包含初始化(循环等)方法和事件回调。由于应用程序可以随时创建并初始化该对象,因此您的第二个问题会自动解决。

您的设计可能会节省几行代码来创建新的应用程序对象,但代价是维护具有潜在长继承线的汤状主对象。

使用接口和回调。

PS:更不用说在构造函数中调用虚函数并不意味着您可能期望的那样。

You are mixing two unrelated functions in one class here. First, it serves as a syntactic shortcut for declaring and initializing StateManager and InputSystem members. Second, it declares abstract create_window function.

If you think there should be a common interface - write an interface (pure abstract class).

Additionally, write something like OgreManager self-contained class with initialization (looping etc) methods and event callbacks. Since applications could create and initialize this object at any moment, your second question is solved automatically.

Your design may save a few lines of code for creating new application objects, but the price is maintaining soup-like master object with potentially long inheritance line.

Use interfaces and callbacks.

P.S.: not to mention that calling virtual functions in constructor doesn't mean what you probably expect.

胡大本事 2024-08-27 03:36:59

是的,这是一个很好的设计,也是我自己使用的一个。

对于你的第二个问题,我将从基本构造函数中删除任何可能不适用于派生类的内容。如果 OGRE 想要自己创建窗口,那么您需要允许它这样做,并且我认为在 createWindow 中初始化 OGRE 是没有意义的(这是误导性的)。

您可以添加初始化渲染系统虚拟方法,但我认为您应该将该任务留给派生类的构造函数。应用程序初始化始终是一项棘手的任务,而且真的非常难以抽象。根据我的经验,最好不要对派生类可能想要做什么做出任何假设,而只是让它以任何它想要的方式自行完成工作。

也就是说,如果您能想到绝对适用于任何可以想象的派生类的东西,那么可以随意将其添加到基本构造函数中。

Yes, that is a good design, and it is one that I use myself.

For your second question, I would remove anything from the base constructor that has any possibility of not being applicable to a derived class. If OGRE wants to create the window itself then you need to allow it to do that, and I don't think that it makes sense to initialize OGRE in createWindow (it's misleading).

You could add an initialize render system virtual method, but I think you should just leave that task to the derived class's constructor. Application initialization is always a tricky task, and really, really difficult to abstract. From my experience, it's best not to make any assumptions about what the derived class might want to do, and just let it do the work itself in any way that it wants.

That said, if you can think of something that will absolutely apply to any conceivable derived class then feel free to add that to the base constructor.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文