std::unique_ptr for class 数据成员 ABI(Pimpl 惯用语)
std::unique_ptr for class data member ABI (Pimpl idiom)
我正在尝试定义使用 "pimpl" 数据成员的现有代码,这些数据成员将被定义为 unique_ptr。有些对象需要自定义删除器,有些则不需要。
unique_ptr(不像shared_ptr)析构函数需要知道对象的完整类型。所以需要在数据成员声明中指定删除器:
class Foo {
public:
...
~Foo (void) //in source file =default
private:
class FooImpl;
std::unique_ptr <FooImpl> _pimpl;
};
在实例化 pimpl 时,您只能使用默认删除器。如果要自定义删除器,需要在声明中指定
class Foo {
public:
...
~Foo (void) //in source file =default
private:
class FooImpl;
std::unique_ptr <FooImpl, std::function <void (FooImpl*&)> > _pimpl;
};
但是,无论您是想要具有默认行为的 unique_ptr d'tor 还是自定义删除器,您都无法灵活选择。更灵活的选项是第二个版本,但如果您选择保持默认行为,则必须使用等同于默认删除的特定删除器实例化 unique_ptr,例如:
Foo::Foo (void) :
_impl (new CurveItemWidgetImpl, std::default_delete <FooImpl> ()) {
}
那么,std::unique_ptr 是处理 ABI 的最佳方式吗(与 shared_ptr 或原始指针相比)?
您可以轻松提供有效调用不透明符号的删除器:
class Foo {
public:
~Foo(); // = default
private:
class FooImpl;
struct FooImplDelete { void operator()(FooImpl*); };
std::unique_ptr<FooImpl, FooImplDelete> _pimpl;
};
现在您可以将 Foo::FooImplDelete::operator()
的定义移动到您的源文件中。实际上,优化编译器会将其内联到 Foo
.
的析构函数中
如果您没有特别的理由怀疑需要自定义删除器,您不妨使用默认值;如果您需要将自定义删除器更改为 release
和 unique_ptr
:
Foo::Foo() try
: _pimpl(new FooImpl)
{
}
catch(...)
{
delete _pimpl.release();
}
Foo::~Foo()
{
delete _pimpl.release();
}
这里有 2 种兼容且可移植的方式。
这首先是简单地自己管理内存(你会发现这很简单)。第二个利用 unique_ptr
自行管理:
class Foo
{
public:
Foo();
Foo(Foo&& rhs) noexcept;
Foo(const Foo&) = delete;
Foo& operator=(Foo&& rhs) noexcept;
Foo& operator=(const Foo&) = delete;
~Foo() noexcept;
private:
class impl;
impl* _pimpl = nullptr;
};
// implementation:
class Foo::impl
{
// TODO: implement your class here
};
// example constructor
Foo::Foo()
: _pimpl { new impl {} }
{}
Foo::Foo(Foo&& rhs) noexcept
: _pimpl { rhs._pimpl }
{
rhs._pimpl = nullptr;
}
Foo& Foo::operator=(Foo&& rhs) noexcept
{
using std::swap;
swap(_pimpl, rhs._pimpl);
}
Foo::~Foo() noexcept
{
// perform custom actions here, like moving impl onto a queue
// or just delete it
delete _pimpl;
}
使用 unique_ptr:
Foo.h:
#ifndef __included__foo__h__
#define __included__foo__h__
#include <memory>
class Foo
{
public:
Foo();
~Foo();
private:
class impl;
std::unique_ptr<impl, void (*) (impl*) noexcept> _pimpl;
static void handle_delete(impl*) noexcept;
};
#endif
Foo.cpp:
#include "Foo.h"
class Foo::impl
{
// TODO: implement your class here
};
// example constructor
Foo::Foo()
: _pimpl { new impl, &Foo::handle_delete }
{
}
Foo::~Foo() = default;
void Foo::handle_delete(impl* p) noexcept
{
// do whatever you need to do with p
// or just delete it
delete p;
}
我正在尝试定义使用 "pimpl" 数据成员的现有代码,这些数据成员将被定义为 unique_ptr。有些对象需要自定义删除器,有些则不需要。
unique_ptr(不像shared_ptr)析构函数需要知道对象的完整类型。所以需要在数据成员声明中指定删除器:
class Foo {
public:
...
~Foo (void) //in source file =default
private:
class FooImpl;
std::unique_ptr <FooImpl> _pimpl;
};
在实例化 pimpl 时,您只能使用默认删除器。如果要自定义删除器,需要在声明中指定
class Foo {
public:
...
~Foo (void) //in source file =default
private:
class FooImpl;
std::unique_ptr <FooImpl, std::function <void (FooImpl*&)> > _pimpl;
};
但是,无论您是想要具有默认行为的 unique_ptr d'tor 还是自定义删除器,您都无法灵活选择。更灵活的选项是第二个版本,但如果您选择保持默认行为,则必须使用等同于默认删除的特定删除器实例化 unique_ptr,例如:
Foo::Foo (void) :
_impl (new CurveItemWidgetImpl, std::default_delete <FooImpl> ()) {
}
那么,std::unique_ptr 是处理 ABI 的最佳方式吗(与 shared_ptr 或原始指针相比)?
您可以轻松提供有效调用不透明符号的删除器:
class Foo {
public:
~Foo(); // = default
private:
class FooImpl;
struct FooImplDelete { void operator()(FooImpl*); };
std::unique_ptr<FooImpl, FooImplDelete> _pimpl;
};
现在您可以将 Foo::FooImplDelete::operator()
的定义移动到您的源文件中。实际上,优化编译器会将其内联到 Foo
.
如果您没有特别的理由怀疑需要自定义删除器,您不妨使用默认值;如果您需要将自定义删除器更改为 release
和 unique_ptr
:
Foo::Foo() try
: _pimpl(new FooImpl)
{
}
catch(...)
{
delete _pimpl.release();
}
Foo::~Foo()
{
delete _pimpl.release();
}
这里有 2 种兼容且可移植的方式。
这首先是简单地自己管理内存(你会发现这很简单)。第二个利用 unique_ptr
自行管理:
class Foo
{
public:
Foo();
Foo(Foo&& rhs) noexcept;
Foo(const Foo&) = delete;
Foo& operator=(Foo&& rhs) noexcept;
Foo& operator=(const Foo&) = delete;
~Foo() noexcept;
private:
class impl;
impl* _pimpl = nullptr;
};
// implementation:
class Foo::impl
{
// TODO: implement your class here
};
// example constructor
Foo::Foo()
: _pimpl { new impl {} }
{}
Foo::Foo(Foo&& rhs) noexcept
: _pimpl { rhs._pimpl }
{
rhs._pimpl = nullptr;
}
Foo& Foo::operator=(Foo&& rhs) noexcept
{
using std::swap;
swap(_pimpl, rhs._pimpl);
}
Foo::~Foo() noexcept
{
// perform custom actions here, like moving impl onto a queue
// or just delete it
delete _pimpl;
}
使用 unique_ptr:
Foo.h:
#ifndef __included__foo__h__
#define __included__foo__h__
#include <memory>
class Foo
{
public:
Foo();
~Foo();
private:
class impl;
std::unique_ptr<impl, void (*) (impl*) noexcept> _pimpl;
static void handle_delete(impl*) noexcept;
};
#endif
Foo.cpp:
#include "Foo.h"
class Foo::impl
{
// TODO: implement your class here
};
// example constructor
Foo::Foo()
: _pimpl { new impl, &Foo::handle_delete }
{
}
Foo::~Foo() = default;
void Foo::handle_delete(impl* p) noexcept
{
// do whatever you need to do with p
// or just delete it
delete p;
}