Is custom deleter for std::unique_ptr a valid plac

2019-07-25 17:08发布

问题:

I have a very basic implementation of reflection that includes a Type class which does object instantiation for the class it describes. Stripped down to the relevant parts, it looks like this:

Type.h:

class Plugin; // forward declaration

typedef std::unique_ptr<Plugin> PluginPtr;

namespace Reflection {

    class Type {
    public:
        explicit Type(PluginPtr(*)());
        PluginPtr CreateInstance();
    private:
        PluginPtr(*_createInstance_Handler)();
    };

}

Type.cpp:

Type::Type(PluginPtr(*createInstance_Handler)()) :
    _createInstance_Handler(createInstance_Handler) {}

PluginPtr CreateInstance() { return (*_createInstance_Handler)(); }

The actual instantiation logic is housed in the Plugin class (and also in each of its descendants):

Plugin.h:

class Plugin {
public:
    virtual ~Plugin();
    static const Reflection::Type Type;
private:
    static PluginPtr CreateInstance();

Plugin.cpp

Plugin::~Plugin() {}

const Reflection::Type Plugin::Type(CreateInstance);

PluginPtr Plugin::CreateInstance() { return PluginPtr(new Plugin); }

When I attempt to compile this, I get these errors (in Visual Studio 2013):

error C2027: use of undefined type 'Plugin'
error C2338: can't delete an incomplete type
warning C4150: deletion of pointer to incomplete type 'Plugin'; no destructor called

I dug around a bit, and apparently this is caused by the deleter of the std::unique_ptr (finding itself inside the class definition of the class it is operating on). I read somewhere that if I supply my own deleter, this problem goes away. So I redefined PluginPtr to this:

typedef std::unique_ptr<Plugin, PluginDeleter> PluginPtr

The (compilation) problem does indeed go away, but the question then, is can/should this PluginDeleter call ~Plugin() manually (to ensure that the plugin (and any derived object that the PluginPtr might be pointing to!) is properly destructed)? And where/how should I best declare/define it, so that I don't get the same problem with incomplete types?

(or is there a better way altogether?)

PS. Working on my source code now, I realize that there's an error in the above code. The last line in Type.cpp should read

    PluginPtr CreateInstance() { return (_createInstance_Handler)(); }

回答1:

The deleter of std::unique_ptr should delete the object, that is to say destroy it (as you can assume it should be), then free the memory used if needed.

If your custom deleter uses the delete operator, then you don't have to manually call the destructor as:

delete is an operator with a very specific behavior: An expression with the delete operator, first calls the appropriate destructor (for class types), and then calls function operator delete (i.e., this function) to release the storage.

If you create the pointer using statically allocated memory or a placement-new in a statically allocated memory or a memory you won't free until the application exit (for example), then you should not call the delete operator but you still have to destroy the object, thus you have to call the destructor of the object.