C++智能指针(2):unique_ptr
分析
在使用 AutoPointer 的时候会发生所有权转移和内存泄漏的问题,所以我们可以对 AutoPointer 类稍加修改,修复这两个问题。
所有权转移
为了规避可能发生所有权转移的情况,我们可以直接禁止它使用拷贝构造函数和赋值操作符。
UniquePointer(UniquePointer<T> &other) = delete;
UniquePointer<T> &operator=(const UniquePointer<T> &other) = delete;
但很多时候我们都需要使用到传递指针的操作,如果只是使用 deleted 函数禁止拷贝构造函数和赋值操作符,那么这个智能指针存在的意义就不大了,我们可以通过 move 语义来实现移动构造函数和移动赋值操作符,从而在使用 UniquePointer 的时候可以在特定情况下进行所有权转移。
UniquePointer(UniquePointer<T> &&other) noexcept;
UniquePointer &operator=(UniquePointer &&other) noexcept;
内存泄漏
为了防止发生内存泄漏,我们可以在UniquePointer的私有成员中增加一个删除器,并根据当前指针对象的类型指定删除器,从而防止发生内存泄漏。
class Deleter {
template<typename T>
void operator()(T *p) {
if (p)
delete p;
}
};
template<typename T, typename D>
class UniquePointer {
...
private:
T *pointer;
Deleter deleter;
};
实现
根据unique_ptr的源码,能够大致实现UniquePointer类
template<typename T, typename D>
class UniquePointer {
public:
explicit UniquePointer(T *t, const D &d);
~UniquePointer();
T &operator*();
T *operator->();
T *release();
void reset(T *p);
UniquePointer(UniquePointer &&other) noexcept;
UniquePointer &operator=(UniquePointer &&other) noexcept;
UniquePointer(const UniquePointer &other) = delete;
UniquePointer &operator=(const UniquePointer &other) = delete;
private:
T *pointer;
D deleter;
};
template<typename T, typename D>
UniquePointer<T, D>::UniquePointer(T *t, const D &d) {
std::cout << "UniquePointer " << this << " constructor called." << std::endl;
this->pointer = t;
this->deleter = d;
}
template<typename T, typename D>
UniquePointer<T, D>::~UniquePointer() {
std::cout << "UniquePointer " << this << " destructor called." << std::endl;
deleter(this->pointer);
}
template<typename T, typename D>
T &UniquePointer<T, D>::operator*() {
return *this->pointer;
}
template<typename T, typename D>
T *UniquePointer<T, D>::operator->() {
return this->pointer;
}
template<typename T, typename D>
T *UniquePointer<T, D>::release() {
T *new_pointer = this->pointer;
this->pointer = nullptr;
return new_pointer;
}
template<typename T, typename D>
void UniquePointer<T, D>::reset(T *p) {
if (this->pointer != p) {
deleter(this->pointer);
this->pointer = p;
}
}
template<typename T, typename D>
UniquePointer<T, D>::UniquePointer(UniquePointer<T, D> &&other) noexcept {
std::cout << "UniquePointer " << this << " move constructor called." << std::endl;
this->pointer = other.release();
deleter(std::move(other.deleter));
}
template<typename T, typename D>
UniquePointer<T, D> &UniquePointer<T, D>::operator=(UniquePointer<T, D> &&other) noexcept {
std::cout << "UniquePointer " << this << " assignment operator called." << std::endl;
if (this->pointer != other.pointer) {
reset(other.release());
deleter = std::move(other.deleter);
}
return *this;
}
测试
尝试使用移动构造函数
class Deleter {
public:
template<typename T>
void operator()(T *p) {
if (p)
delete p;
}
};
int main() {
Deleter deleter;
Obj *o = new Obj();
UniquePointer<Obj, Deleter> u1(o, deleter);
UniquePointer<Obj, Deleter> u2(move(u1));
return 0;
}
/*
output:
Construct
UniquePointer 0x7ffee7dada08 constructor called.
UniquePointer 0x7ffee7dad9f8 move constructor called.
UniquePointer 0x7ffee7dad9f8 destructor called.
Destruct
UniquePointer 0x7ffee7dada08 destructor called.
*/
尝试使用移动赋值操作符
class Deleter {
public:
template<typename T>
void operator()(T *p) {
if (p)
delete p;
}
};
int main() {
Deleter deleter;
Obj *o = new Obj();
UniquePointer<Obj, Deleter> u1(o, deleter);
UniquePointer<Obj, Deleter> u2(nullptr, deleter);
u2 = move(u1);
return 0;
}
/*
output:
Construct
UniquePointer 0x7ffee915da08 constructor called.
UniquePointer 0x7ffee915d9f8 constructor called.
UniquePointer 0x7ffee915d9f8 assignment operator called.
UniquePointer 0x7ffee915d9f8 destructor called.
Destruct
UniquePointer 0x7ffee915da08 destructor called.
*/
定义一个数组删除器,尝试以数组指针初始化UniquePointer类对象
class ArrayDeleter {
public:
template<typename T>
void operator()(T *p) {
if (p)
delete[] p;
}
};
int main() {
ArrayDeleter array_deleter;
Obj *o = new Obj[3];
UniquePointer<Obj, ArrayDeleter> u(o, array_deleter);
return 0;
}
/*
output:
Construct
Construct
Construct
UniquePointer 0x7ffeed926a08 constructor called.
UniquePointer 0x7ffeed926a08 destructor called.
Destruct
Destruct
Destruct
*/
作为对比,如果使用默认删除器作为数组指针的删除器
class Deleter {
public:
template<typename T>
void operator()(T *p) {
if (p)
delete p;
}
};
int main() {
Deleter deleter;
Obj *o = new Obj[3];
UniquePointer<Obj, Deleter> u(o, deleter);
return 0;
}
/*
output:
Construct
Construct
Construct
UniquePointer 0x7ffee8f85a10 constructor called.
UniquePointer 0x7ffee8f85a10 destructor called.
Destruct
*/
说明删除器能够正确地修复内存泄漏的问题。
尝试将两个UniquePointer的对象指向同一个指针
int main() {
Deleter deleter;
Obj *o = new Obj();
UniquePointer<Obj, Deleter> u1(o, deleter);
UniquePointer<Obj, Deleter> u2(o, deleter);
return 0;
}
/*
output:
(19576,0x10e00a5c0) malloc: *** error for object 0x7fcfe8c02ab0: pointer being freed was not allocated
(19576,0x10e00a5c0) malloc: *** set a breakpoint in malloc_error_break to debug
Construct
UniquePointer 0x7ffee28a9a10 constructor called.
UniquePointer 0x7ffee28a9a00 constructor called.
UniquePointer 0x7ffee28a9a00 destructor called.
Destruct
UniquePointer 0x7ffee28a9a10 destructor called.
Destruct
*/
还是产生调用两次析构函数的错误。
总结
UniquePointer成功地解决了所有权转移和内存泄漏的问题,但还有诸如重复析构的问题存在。
unique_ptr源码
template <class _Tp, class _Dp = default_delete<_Tp> >
class _LIBCPP_TEMPLATE_VIS unique_ptr {
public:
typedef _Tp element_type;
typedef _Dp deleter_type;
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
static_assert(!is_rvalue_reference<deleter_type>::value,
"the specified deleter type cannot be an rvalue reference");
private:
__compressed_pair<pointer, deleter_type> __ptr_;
struct __nat { int __for_bool_; };
#ifndef _LIBCPP_CXX03_LANG
typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
template <bool _Dummy>
using _LValRefType =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type;
template <bool _Dummy>
using _GoodRValRefType =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type;
template <bool _Dummy>
using _BadRValRefType =
typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type;
template <bool _Dummy, class _Deleter = typename __dependent_type<
__identity<deleter_type>, _Dummy>::type>
using _EnableIfDeleterDefaultConstructible =
typename enable_if<is_default_constructible<_Deleter>::value &&
!is_pointer<_Deleter>::value>::type;
template <class _ArgType>
using _EnableIfDeleterConstructible =
typename enable_if<is_constructible<deleter_type, _ArgType>::value>::type;
template <class _UPtr, class _Up>
using _EnableIfMoveConvertible = typename enable_if<
is_convertible<typename _UPtr::pointer, pointer>::value &&
!is_array<_Up>::value
>::type;
template <class _UDel>
using _EnableIfDeleterConvertible = typename enable_if<
(is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) ||
(!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value)
>::type;
template <class _UDel>
using _EnableIfDeleterAssignable = typename enable_if<
is_assignable<_Dp&, _UDel&&>::value
>::type;
public:
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy>>
_LIBCPP_INLINE_VISIBILITY
constexpr unique_ptr() noexcept : __ptr_(pointer()) {}
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy>>
_LIBCPP_INLINE_VISIBILITY
constexpr unique_ptr(nullptr_t) noexcept : __ptr_(pointer()) {}
template <bool _Dummy = true,
class = _EnableIfDeleterDefaultConstructible<_Dummy>>
_LIBCPP_INLINE_VISIBILITY
explicit unique_ptr(pointer __p) noexcept : __ptr_(__p) {}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_LValRefType<_Dummy>>>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _LValRefType<_Dummy> __d) noexcept
: __ptr_(__p, __d) {}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_GoodRValRefType<_Dummy>>>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _GoodRValRefType<_Dummy> __d) noexcept
: __ptr_(__p, _VSTD::move(__d)) {
static_assert(!is_reference<deleter_type>::value,
"rvalue deleter bound to reference");
}
template <bool _Dummy = true,
class = _EnableIfDeleterConstructible<_BadRValRefType<_Dummy>>>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, _BadRValRefType<_Dummy> __d) = delete;
_LIBCPP_INLINE_VISIBILITY
unique_ptr(unique_ptr&& __u) noexcept
: __ptr_(__u.release(), _VSTD::forward<deleter_type>(__u.get_deleter())) {
}
template <class _Up, class _Ep,
class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _Up>,
class = _EnableIfDeleterConvertible<_Ep>
>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT
: __ptr_(__u.release(), _VSTD::forward<_Ep>(__u.get_deleter())) {}
#if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR)
template <class _Up>
_LIBCPP_INLINE_VISIBILITY
unique_ptr(auto_ptr<_Up>&& __p,
typename enable_if<is_convertible<_Up*, _Tp*>::value &&
is_same<_Dp, default_delete<_Tp>>::value,
__nat>::type = __nat()) _NOEXCEPT
: __ptr_(__p.release()) {}
#endif
_LIBCPP_INLINE_VISIBILITY
unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
reset(__u.release());
__ptr_.second() = _VSTD::forward<deleter_type>(__u.get_deleter());
return *this;
}
template <class _Up, class _Ep,
class = _EnableIfMoveConvertible<unique_ptr<_Up, _Ep>, _Up>,
class = _EnableIfDeleterAssignable<_Ep>
>
_LIBCPP_INLINE_VISIBILITY
unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT {
reset(__u.release());
__ptr_.second() = _VSTD::forward<_Ep>(__u.get_deleter());
return *this;
}
#else // _LIBCPP_CXX03_LANG
private:
unique_ptr(unique_ptr&);
template <class _Up, class _Ep> unique_ptr(unique_ptr<_Up, _Ep>&);
unique_ptr& operator=(unique_ptr&);
template <class _Up, class _Ep> unique_ptr& operator=(unique_ptr<_Up, _Ep>&);
public:
_LIBCPP_INLINE_VISIBILITY
unique_ptr() : __ptr_(pointer())
{
static_assert(!is_pointer<deleter_type>::value,
"unique_ptr constructed with null function pointer deleter");
static_assert(is_default_constructible<deleter_type>::value,
"unique_ptr::deleter_type is not default constructible");
}
_LIBCPP_INLINE_VISIBILITY
unique_ptr(nullptr_t) : __ptr_(pointer())
{
static_assert(!is_pointer<deleter_type>::value,
"unique_ptr constructed with null function pointer deleter");
}
_LIBCPP_INLINE_VISIBILITY
explicit unique_ptr(pointer __p)
: __ptr_(_VSTD::move(__p)) {
static_assert(!is_pointer<deleter_type>::value,
"unique_ptr constructed with null function pointer deleter");
}
_LIBCPP_INLINE_VISIBILITY
operator __rv<unique_ptr>() {
return __rv<unique_ptr>(*this);
}
_LIBCPP_INLINE_VISIBILITY
unique_ptr(__rv<unique_ptr> __u)
: __ptr_(__u->release(),
_VSTD::forward<deleter_type>(__u->get_deleter())) {}
template <class _Up, class _Ep>
_LIBCPP_INLINE_VISIBILITY
typename enable_if<
!is_array<_Up>::value &&
is_convertible<typename unique_ptr<_Up, _Ep>::pointer,
pointer>::value &&
is_assignable<deleter_type&, _Ep&>::value,
unique_ptr&>::type
operator=(unique_ptr<_Up, _Ep> __u) {
reset(__u.release());
__ptr_.second() = _VSTD::forward<_Ep>(__u.get_deleter());
return *this;
}
_LIBCPP_INLINE_VISIBILITY
unique_ptr(pointer __p, deleter_type __d)
: __ptr_(_VSTD::move(__p), _VSTD::move(__d)) {}
#endif // _LIBCPP_CXX03_LANG
#if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR)
template <class _Up>
_LIBCPP_INLINE_VISIBILITY
typename enable_if<is_convertible<_Up*, _Tp*>::value &&
is_same<_Dp, default_delete<_Tp> >::value,
unique_ptr&>::type
operator=(auto_ptr<_Up> __p) {
reset(__p.release());
return *this;
}
#endif
_LIBCPP_INLINE_VISIBILITY
~unique_ptr() { reset(); }
_LIBCPP_INLINE_VISIBILITY
unique_ptr& operator=(nullptr_t) _NOEXCEPT {
reset();
return *this;
}
_LIBCPP_INLINE_VISIBILITY
typename add_lvalue_reference<_Tp>::type
operator*() const {
return *__ptr_.first();
}
_LIBCPP_INLINE_VISIBILITY
pointer operator->() const _NOEXCEPT {
return __ptr_.first();
}
_LIBCPP_INLINE_VISIBILITY
pointer get() const _NOEXCEPT {
return __ptr_.first();
}
_LIBCPP_INLINE_VISIBILITY
deleter_type& get_deleter() _NOEXCEPT {
return __ptr_.second();
}
_LIBCPP_INLINE_VISIBILITY
const deleter_type& get_deleter() const _NOEXCEPT {
return __ptr_.second();
}
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_EXPLICIT operator bool() const _NOEXCEPT {
return __ptr_.first() != nullptr;
}
_LIBCPP_INLINE_VISIBILITY
pointer release() _NOEXCEPT {
pointer __t = __ptr_.first();
__ptr_.first() = pointer();
return __t;
}
_LIBCPP_INLINE_VISIBILITY
void reset(pointer __p = pointer()) _NOEXCEPT {
pointer __tmp = __ptr_.first();
__ptr_.first() = __p;
if (__tmp)
__ptr_.second()(__tmp);
}
_LIBCPP_INLINE_VISIBILITY
void swap(unique_ptr& __u) _NOEXCEPT {
__ptr_.swap(__u.__ptr_);
}
};