开发者

Should I make wrappers for polymorphic classes that need to be allocatd on the heap?

开发者 https://www.devze.com 2023-02-26 04:51 出处:网络
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:

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消