我应该为我的不透明对象使用整数 ID 还是指针? [英] Should I use integer ID or pointers for my opaque objects?

查看:23
本文介绍了我应该为我的不透明对象使用整数 ID 还是指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在一些图形 API(DirectX9 和 DirectX11)之上编写一个抽象层,我想听听您的意见.

I'm writing an abstraction layer on top of some graphics API (DirectX9 and DirectX11) and I would like your opinion.

传统上,我会为我想要抽象的每个概念创建一个基类.
因此,在典型的 OO 方式中,我将拥有一个类 Shader 和 2 个子类 DX9Shader 和 DX11Shader.

Traditionally I would create a base class for each concept I want to abstract.
So in typical OO fashion I would have for example a class Shader and 2 subclasses DX9Shader and DX11Shader.

我会重复纹理等的过程......当我需要实例化它们时,我有一个抽象工厂,它将根据当前的图形 API 返回适当的子类.
在 RAII 之后,返回的指针将封装在 std::shared_ptr 中.

I would repeat the process for textures, etc... and when I need to instantiate them I have an abstract factory that will return the appropriate subclass depending on the current graphics API.
Following RAII, the returned pointer would be encapsulated in a std::shared_ptr.

到目前为止一切顺利,但就我而言,这种方法存在一些问题:

So far so good but in my case there are a few problems with this approach:

  1. 我需要提出一个公共接口来封装这两个 API(以及未来的其他 API)的功能.
  2. 派生类存储在单独的 DLL 中(一个用于 DX9,一个用于 DX11 等...)并且在客户端中为它们提供一个 shared_ptr 是一个诅咒:退出时图形 dll 被卸载,如果客户端仍然有boom 图形对象之一的 shared_ptr,由于从卸载的 DLL 调用代码而崩溃.
  1. I need to come up with a public interface that encapsulate the functionality of both APIs (and other APIs in the future).
  2. The derived class are stored in separate DLLs (one for DX9, one for DX11 etc...) and having a shared_ptr to them in the client is a curse: on exit the graphic dlls are unloaded and if the client still has a shared_ptr to one of the graphics objects boom, crash due to calling code from unloaded DLL.

这促使我重新设计我做事的方式:我以为我可以只返回指向资源的原始指针并在其之后清理图形 API,但仍然存在客户端悬空指针和接口问题的问题.我什至考虑过像 COM 一样手动引用计数,但我认为这会倒退(如果我错了,请纠正我,来自 shared_ptr 世界,手动引用计数似乎很原始).

This prompted me to re-design the way I do things: I thought I could just return raw pointers to the resources and have the graphics API clean after itself but there's still the issue of dangling pointers on the client side and the interface problem. I even considered manual reference counting like COM but I thought that would be a step backwards (correct me if I'm wrong, coming from the shared_ptr world, manual reference counting seems primitive).

然后我看到了 Humus 的作品,他所有的图形类都用整数 ID 表示(很像 OpenGL 所做的).创建一个新对象只返回它的整数ID,并在内部存储指针;完全不透明!

Then I saw the work of Humus where all his graphics classes are represented by integer IDs (much like what OpenGL does). Creating a new object only returns its integer ID, and stores the pointer internally; it's all perfectly opaque!

代表抽象的类(例如 DX9Shader 等...)都隐藏在设备 API 之后,这是唯一的接口.
如果要设置纹理,只需调用 device->SetTexture(ID) 即可,其余的在幕后进行.

The classes that represent the abstraction (such as DX9Shader etc...) are all hidden behind the device API which is the only interface.
If one wants to set a texture, it's just a matter of calling device->SetTexture(ID) and the rest happens behind the scenes.

缺点是 API 的隐藏部分过于臃肿,需要大量样板代码才能使其工作,而且我不喜欢全能类.

The downfall is that the hidden part of the API is bloated, there is a lot of boiler plate code required to make it work and I'm not a fan of a do-it-all class.

任何想法/想法?

推荐答案

您说主要问题是卸载 DLL 时仍然有指向其内部的指针.嗯……不要那样做.您有一个类实例,其成员在该 DLL 中实现.只要这些类实例存在,卸载该 DLL 基本上就是一个错误.

You say that the main problem is that a DLL is unloaded while still having a pointer to its internals. Well... don't do that. You have a class instance, who's members are implemented in that DLL. It is fundamentally an error for that DLL to be unloaded so long as those class instances exist.

因此,您需要对如何使用此抽象负责.正如您需要对从 DLL 加载的任何代码负责:在卸载 DLL 之前,必须清除来自 DLL 的内容.你如何做到这一点取决于你.您可以拥有一个内部引用计数,该计数为 DLL 返回的每个对象递增,并且仅在所有引用的对象消失后才卸载 DLL.或者任何东西,真的.

You therefore need to be responsible in how you use this abstraction. Just as you need to be responsible with any code you load from a DLL: stuff that comes from the DLL must be cleaned up before you unload the DLL. How you do that is up to you. You could have an internal reference count that gets incremented for every object the DLL returns and only unload the DLL after all referenced objects go away. Or anything, really.

毕竟,即使您使用这些不透明的数字或其他任何东西,如果在卸载 DLL 时在该数字上调用这些 API 函数之一,会发生什么情况?哎呀...所以它并没有真正为您提供任何保护.无论哪种方式,您都必须负责.

After all, even if you use these opaque numbers or whatever, what happens if you call one of those API functions on that number when the DLL is unloaded? Oops... So it doesn't really buy you any protection. You have to be responsible either way.

您可能没有想到的数字方法的缺点是:

The downsides of the number method that you may not be thinking about are:

  • 降低了解对象实际是什么的能力.API 调用可能会失败,因为您传递的数字并不是真正的对象.或者更糟的是,如果你将一个着色器对象传递给一个接受纹理的函数会发生什么?也许我们正在谈论一个接受着色器和纹理的函数,而您不小心忘记了参数的顺序?如果那些是对象指针,C++ 的规则甚至不允许该代码编译.但是用整数?都很好;你只会得到运行时错误.

  • Reduced ability to know what an object actually is. API calls can fail because you passed a number that isn't really an object. Or worse, what happens if you pass a shader object into a function that takes a texture? Maybe we're talking about a function that takes a shader and a texture, and you accidentally forget the order of the arguments? The rules of C++ wouldn't allow that code to even compile if those were object pointers. But with integers? It's all good; you'd only get runtime errors.

性能.每个 API 调用都必须在哈希表或其他东西中查找这个数字,以获取要使用的实际指针.如果它是一个哈希表(即:一个数组),那么它可能相当小.但它仍然是一个间接的.而且由于您的抽象看起来非常低级,因此在此级别的任何性能损失都会在性能关键的情况下真正受到伤害.

Performance. Every API call will have to look this number up in a hashtable or something to get an actual pointer to work with. If it's a hashtable (ie: an array), then it's probably fairly minor. But it's still an indirection. And since your abstraction seems very low-level, any performance loss at this level can really hurt in performance-critical situations.

缺乏 RAII 和其他范围界定机制.当然,您可以编写一个 shared_ptr-esque 对象来创建和删除它们.但是,如果您使用的是实际指针,则不必这样做.

Lack of RAII and other scoping mechanisms. Sure, you could write a shared_ptr-esque object that would create and delete them. But you wouldn't have to do that if you were using an actual pointer.

只是看起来不值得.

这篇关于我应该为我的不透明对象使用整数 ID 还是指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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