两步对象销毁背后的原因是什么? [英] What is the reasoning behind two-step object destruction?

查看:24
本文介绍了两步对象销毁背后的原因是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在游戏开发世界中,我经常看到使用单独的 initialize()uninitialize()shutdown() 方法的类.这不仅包括多个教程,还包括历史悠久的大型现实项目,如一些现代游戏引擎.我最近在 Cry Engine 3 中看到一个类,它不仅使用 shutdown() 方法,而且还从它调用 this.~Foo() ,基于我对 C++ 的了解,这不能算是一个好的设计.

In the games development world I often see classes using separate initialize() and uninitialize() or shutdown() methods. This includes not only multiple tutorials, but also long-established and large real-world projects, like some modern game engines. I've recently seen a class in Cry Engine 3 which not only uses a shutdown() method, but goes as far as calling this.~Foo() from it, which based on everything I know about C++, can't really be considered a good design.

虽然我可以看到两步初始化带来的一些好处,并且有很多关于它的讨论,但我无法理解两步销毁背后的推理.为什么不以析构函数的形式使用 C++ 语言提供的默认工具,而是有一个单独的 shutdown() 方法并且析构函数留空呢?为什么不更进一步,使用现代 C++,将对象持有的所有资源放入智能指针中,这样我们就不必担心手动释放它们.

While I can see some of the benefits that come from two-step initialization, and there are many discussions about it, I can't understand the reasoning behind the two-step destruction. Why not use the default facilities provided by C++ language in form of the destructor, but have a separate shutdown() method and the destructor left empty? Why not go even further and, using modern C++, put all the resources held by an object into smart pointers so we don't have to worry about releasing them manually.

两步销毁是否是一些基于不再适用的原则的过时设计,或者是否有一些合理的理由在控制对象生命周期的标准方式上使用它?

Is two-step destruction some outdated design based on principles that no longer apply or are there some valid reasons to use it over standard ways of controlling objects' lifetime?

推荐答案

如果您不想阅读,要点是您需要异常才能从 ctors 返回错误,而异常是错误的.

If you don't want to read, the gist is that you need exceptions to return errors from ctors and exceptions are bad.

正如 Trevor 和其他人所暗示的那样,这种做法有很多原因.你已经提出了这里有一个具体的例子,所以让我们解决这个问题.

As Trevor and others have hinted at, there are a number of reasons for this practice. You've brought up a specific example here though, so let's address that.

本教程处理包含以下定义的类 GraphicsClass(该名称肯定不会激发信心):

The tutorial deals with a class GraphicsClass (the name sure doesn't inspire confidence) which contains these definitions:

class GraphicsClass
{
public:
    GraphicsClass();
    ~GraphicsClass();
    bool Initialize(int, int, HWND);
    void Shutdown();
};

所以它有ctor、dtor 和Initialize/Shutdown.为什么不将后者浓缩为前者呢?该实现提供了一些线索:

So it has ctor, dtor, and Initialize/Shutdown. Why not condense the latter into the former? The implementation gives a few clues:

bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
    bool result;

    // Create the Direct3D object.
    m_D3D = new D3DClass;
    if(!m_D3D)
    {
        return false;
    }

    // Initialize the Direct3D object.
    result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
    if(!result)
    {
        MessageBox(hwnd, L"Could not initialize Direct3D", L"Error", MB_OK);
        return false;
    }

    return true;
}

好的,检查new D3DClass是否失败毫无意义的(只有当我们用完内存我们已经覆盖了new 不抛出 bad_alloc)*.检查以查看 D3DClass::Initialize() 可能不会失败.正如它的签名所暗示的那样,它试图初始化一些与图形硬件相关的资源,这在正常情况下有时会失败——可能请求的分辨率太高,或者资源正在使用中.我们想要优雅地处理它,并且我们不能在构造函数中返回错误,我们只能抛出异常.

Ok sure, checking to see if new D3DClass fails is pointless (it only happens if we run out of memory and we've overridden new to not throw bad_alloc)*. Checking to see D3DClass::Initialize() fails may not be though. As its signature hints at, it's trying to initialise some resources related to graphics hardware, which can sometimes fail in normal circumstances - maybe the resolution requested is too high, or the resource is in use. We'd want to handle that gracefully, and we can't return errors in the ctor, we can only throw exceptions.

这当然提出了一个问题:我们为什么不抛出异常?C++ 异常非常慢.太慢以至于对它的意见非常强烈, 尤其是在游戏开发方面.加上 你不能扔 dtor,所以玩得开心,把网络资源终止放在那里.大多数(如果不是全部)C++ 游戏都是在关闭异常的情况下制作的.

Which of course raises the question: why don't we throw exceptions? C++ exceptions are very slow. So slow that opinions of it are very strong, especially in game development. Plus you can't throw in the dtor, so have fun trying to say, put network resource termination there. Most, if not all, C++ games have been made with exceptions turned off.

无论如何,这是主要原因;不过,我不能忽视其他一些有时更愚蠢的原因,例如拥有 C 遗产(没有 ctors/dtors),或者具有模块 A 和 B 相互引用的架构.当然记得游戏开发的 #1 优先级是发布游戏,而不是创建非常健壮和可维护的架构,因此您有时会看到这样的愚蠢做法.

That's the main reason anyway; I can't discount other, sometimes sillier, reasons though, such as having a C legacy (where there are no ctors/dtors), or an architecture that has pairs of modules A and B hold references to each other. Of course remember games development's #1 priority is to ship games, not create perfectly robust and maintainable architectures, so you sometimes see silly practices like this.

我听说 C++ 委员会深知异常所带来的问题,但 iirc 的最新消息是它被放在了太难"的桶里,所以你会在很多年的游戏中看到更多这样的问题来.

I hear that the C++ committee is deeply aware of the problems that exceptions have, but iirc the latest is that it's been put in the "too hard" bucket, so you'll see more of this in games for many years to come.

*- 啊哈!因此,检查 new D3DClass 是否没有 毫无意义,因为我们可能已经禁用了异常,因此这是检查内存分配失败等的唯一方法.

*- Aha! So checking to see if new D3DClass wasn't pointless, as we've probably disabled exceptions so this is the only way to check for failed memory alloc, among other things.

这篇关于两步对象销毁背后的原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆