可以通过一些间接的方式完成...我来了:)
它基于boost::shared_ptr 的实现,如果我们实际上将两个内存块粘合在一起,而不是持有指向内存的指针,则可以从良好的加速中受益……但随之而来的是对齐问题等等......所以我不会不顾一切地去做。
首先,我们需要一个用于管理内存的类,并在必要时提供自定义解除分配器。
它是间接的,这就是魔法所在。
请注意,它实现了深度复制行为。
namespace detail
{
// The interface
template <class T>
class MemoryOwnerBase
{
public:
virtual ~MemoryOwnerBase() { this->dispose(mItem); mItem = 0; }
virtual void dispose(T* item) = 0;
virtual void clone() const = 0;
T* get() { return mItem; }
T* release() { T* tmp = mItem; mItem = 0; return tmp; }
void reset(T* item = 0)
{
if (mItem && item != mItem) this->dispose(mItem);
mItem = item;
}
protected:
explicit MemoryOwnerBase(T* i = 0): mItem(i) {}
MemoryOwnerBase(const MemoryOwnerBase& rhs): mItem(0)
{
if (rhs.mItem) mItem = new_clone(*rhs.mItem); // Boost Clonable concept
}
MemoryOwnerBase& operator=(const MemoryOwnerBase& rhs)
{
MemoryOwnerBase tmp(rhs);
this->swap(rhs);
return *this;
}
private:
T* mItem;
};
// by default, call delete
template <class T>
struct DefaultDisposer
{
void dispose(T* item) { delete item; }
};
// the real class, the type of the disposer is erased from the point of view
// of its creator
template <class T, class D = DefaultDisposer<T> >
class MemoryOwner: public MemoryOwnerBase, private D // EBO
{
public:
MemoryOwner(): MemoryOwnerBase(0), D() {}
explicit MemoryOwner(T* item): MemoryOwnerBase(item), D() {}
MemoryOwner(T* item, D disposer): MemoryOwnerBase(item), D(disposer) {}
virtual void dispose(T* item) { ((D&)*this).dispose(item); }
virtual MemoryOwner* clone() const { return new MemoryOwner(*this); }
};
// easier with type detection
template <class T>
MemoryOwnerBase<T>* make_owner(T* item)
{
return new MemoryOwner<T>(item);
}
template <class T, class D>
MemoryOwnerBase<T>* make_owner(T* item, D d)
{
return new MemoryOwner<T,D>(item,d);
}
} // namespace detail
然后我们可以制作我们的 Pimpl 类,因为它是你所追求的。
template <class T>
class Pimpl
{
typedef detail::MemoryOwnerBase<T> owner_base;
public:
Pimpl(): mItem(0), mOwner(0) {}
explicit Pimpl(T* item):
mItem(item), mOwner(item == 0 ? 0 : detail::make_owner(item)) {}
template <class D>
Pimpl(T* item, D d):
mItem(item), mOwner(item == 0 ? 0 : detail::make_owner(item, d)) {}
Pimpl(const Pimpl& rhs): mItem(), mOwner()
{
if (rhs.mOwner)
{
mOwner = rhs.mOwner.clone();
mItem = mOwner->get();
}
}
T* get() { return mItem; }
const T* get() const { return mItem; }
void reset(T* item = 0)
{
if (item && !mOwner)
mOwner = detail::make_owner(item);
if (mOwner)
{
mOwner->reset(item);
mItem = mOwner->get();
}
}
template <class D>
void reset(T* item, D d)
{
if (mOwner)
{
if (mItem == item) mOwner->release();
delete mOwner;
}
mOwner = detail::make_owner(item, d);
mItem = item;
}
T* operator->() { return mItem; }
const T* operator->() const { return mItem; }
T& operator*() { return *mItem; }
const T& operator*() const { return *mItem; }
private:
T* mItem; // Proxy for faster memory access
detail::MemoryOwnerBase<T>* mOwner; // Memory owner
}; // class Pimpl
好的,pfiou!
让我们现在使用它:)
// myClass.h
class MyClass
{
public:
MyClass();
private:
struct Impl;
Pimpl<Impl> mImpl;
};
// myClass.cpp
struct MyClass::Impl
{
Impl(): mA(0), mB(0) {}
int mA;
int mB;
};
// Choice 1
// Easy
MyClass::MyClass(): mImpl(new Impl()) {}
// Choice 2
// Special purpose allocator (pool ?)
struct MyAllocatorDeleter
{
void dispose(Impl* item) { /* my own routine */ }
};
MyClass::MyClass(): mImpl(new Impl(), MyAllocatorDeleter()) {}
是的,它很神奇;)
背后的原理是调用Type Erasure。该机制确保一旦构建了MemoryOwner 对象,它就知道如何删除它持有的内存,并通过间接方式向调用者隐藏确切的机制。
因此您可以将Pimpl<T> 对象视为一个值:
- DeepCopy 语义
- DeepConst 语义(
volatile 被忽略...)
- 默认的 CopyConstructor、赋值运算符和析构函数都可以
但要注意它隐藏了一个指针,您的职责是在取消引用它之前确保它是非空的。
如果去掉mOwner参数的惰性初始化,代码可以大大简化。此外,一些例外安全约束:disposer 复制构造函数应该是禁止抛出的,否则所有的赌注都没有。
编辑:
解释。
这里的问题是代码绝缘。无论指向的类型如何,都可以对指针执行许多操作,但是对于创建或销毁,我们需要知道底层类型。
4 种基本方法需要创建和销毁,因此需要了解底层类型:
- 构造函数
- 复制构造函数
- 赋值运算符(旧值的破坏)
- 析构函数
它们本身是实现价值语义所必需的。
在C++ 中,有一个称为type erasure 的习语,它包含在虚拟接口后面嵌入类型信息。因此设计的第一部分:
template <class T> class MemoryOwnerBase {};
template <class T, class D> class MemoryOwner: public MemoryOwnerBase<T> {};
MemoryOwnerBase 提供我们正在寻找的基本操作(构造、深拷贝和销毁),并隐藏类型特定信息(如何正确删除)。
MemoryOwner 实现了MemoryOwnerBase 的virtual 方法,并通过其D(用于处置器)参数封装了销毁指针所需的知识。
现在,为了操作MemoryOwnerBase,我们需要一个指向它的指针/引用,它没有值语义,因此我们将它包装在Pimpl 类中(代表pointer-to-实现)具有适当的值语义。
请注意,只有 a disposer(用于销毁)需要包装,因为用户需要自己提供一个指针,从而使用 new 运算符。
一个改进是提供一个Pimpl<T> make_pimpl<T,D>(const T&, const D&) 方法来查看内存分配等...但由于上述存储对齐问题,我还没有开始。