Right now, the GUI API开发者_StackOverflow中文版 for games I''m developing has an abstract Image and Font class. Depending on whether they use a GL, SDL etc Font, they load fonts like this:
Font *segoe = Font::load("segoe.ttf");
There is also a corresponding destroy() static function as well.
I like this design, but I was wondering if it would be a good idea to wrap it so that no pointers needed. Something like this (with better naming conventions)
WrappedFont segoe = WrappedFont(Font::load("segoe.ttf"));
It would also have overloaded = and a copy ctor, and a destructor.
Is it generally more desired for a game gui for the user to manage their Images and Fonts, or is a wrapped pattern more desirable?
Thanks
Basically, that's a custom smart pointer type to handle the load/destroy
logic. This is very idiomatic and the way to go in C++. Resource management should be automatic.
So your wrapper is fine, but perhaps you are reinventing the wheel here. You may consider rewriting the font system that the work done by destroy
is moved to the Font
destructor, in which case you could use a regular boost::shared_ptr
(also available from std
in C++0x respectively std::tr1
in older compilers with support for the Technical Report 1). With your current code you can use shared_ptr
as well, but you need to supply a custom deleter, which I find a bit cumbersome.
Yes, that's the way to go. Minor point: why not just have
WrappedFont segoe = WrappedFont("segoe.ttf");
You don't need to expose every detail to the user. The simpler it is, the fewer errors. If they really need to know the font involved, add a Font getFont() method.
This is always a personal choice. If you decide to allocate in constructor/init methods and destroy in destructor then it's a good idea (for that you have shared_ptr<>
also.
But be informed that, if it's polymorphic then the virtual calls outside the class scope will be something like,
WrappedFont segoe = WrappedFont(Font::load("segoe.ttf"));
seogoe.getFontPtr()->VirtualFunction(); // more text !
You could yes, but the idea is to simplify the user life.
I usually introduce 2 classes for this:
Font
: a base non-virtual class, which acts as a wrapper and resource manager and delegate the calls to an implementation (internal)FontInterface
: a pure interface
Customization comes by deriving from FontInterface
and then somehow hooking into Font
. In the case where I have full control over the code, I usually ask that an enum member be added for each derived class, the constructor for Font
switches on this enum
to instantiate the right derived class.
This is, indeed, a mix of Strategy
and Factory
pattern:
class Font
{
public:
enum Type { Segoe };
explicit Font(Type type);
void call1() const { _impl->call1(); }
void call2() const { _impl->call2(); }
private:
std::unique_ptr<FontInterface> _impl;
};
What's really interesting here is that the interface is stable in the face of redesign (for the user). The fact that there is an internal implementation which depends from the font selected is completely hidden.
Indeed, it is easy enough to move toward a FlyWeight
pattern, for in the case of fonts the Font
itself is normally stateless, only specifying behavior. Just change the std::unique_ptr
for a std::shared_ptr
, and have a simple cache system to maintain in memory the currently loaded fonts (using weak_ptr
) and hand them over as necessary.
精彩评论