源码分析
源码链接: gcc/unique_ptr.h at master · gcc-mirror/gcc · GitHub
上面链接中的源码是unique_ptr的完整定义,我们来简化其类结构看看:
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
__uniq_ptr_data<_Tp, _Dp> _M_t;
public:
using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
using element_type = _Tp;
using deleter_type = _Dp;
//构造函数
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr() noexcept: _M_t() { }
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
unique_ptr(pointer __p) noexcept : _M_t(__p) { }
template<typename _Del = deleter_type,typename = _Require<is_copy_constructible<_Del>>>
unique_ptr(pointer __p, const deleter_type& __d) noexcept : _M_t(__p, __d) { }
template<typename _Del = deleter_type, typename = _Require<is_move_constructible<_Del>>>
unique_ptr(pointer __p,__enable_if_t<!is_lvalue_reference<_Del>::value,_Del&&> __d)
noexcept : _M_t(__p, std::move(__d)) { }
template<typename _Del = deleter_type,typename _DelUnref = typename remove_reference<_Del>::type>
unique_ptr(pointer,__enable_if_t<is_lvalue_reference<_Del>::value, _DelUnref&&>) = delete;
constexpr unique_ptr(nullptr_t) noexcept : _M_t() { }
// 移动构造函数
unique_ptr(unique_ptr&&) = default;
unique_ptr(unique_ptr<_Up, _Ep>&& __u)
noexcept : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) { }
// 析构函数
~unique_ptr() noexcept {
auto& __ptr = _M_t._M_ptr();
if (__ptr != nullptr)
get_deleter()(std::move(__ptr));
__ptr = pointer();
}
// 操作符 =
unique_ptr& operator=(unique_ptr&&) = default;
template<typename _Up, typename _Ep>
typename enable_if< __and_<__safe_conversion_up<_Up, _Ep>,is_assignable<deleter_type&, _Ep&&>>::value,unique_ptr&>::type
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept {
reset(__u.release());
get_deleter() = std::forward<_Ep>(__u.get_deleter());
return *this;
}
unique_ptr& operator=(nullptr_t) noexcept {
reset();
return *this;
}
//操作符 *
typename add_lvalue_reference<element_type>::type
operator*() const noexcept(noexcept(*std::declval<pointer>())) {
return *get();
}
//操作符 ->
pointer operator->() const noexcept {
_GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
return get();
}
// 获取指针
pointer get() const noexcept
{ return _M_t._M_ptr(); }
// 获取deleter函数
deleter_type& get_deleter() noexcept
{ return _M_t._M_deleter(); }
const deleter_type& get_deleter() const noexcept {
return _M_t._M_deleter();
}
// 操作符 取bool
explicit operator bool() const noexcept {
return get() == pointer() ? false : true;
}
// release接口
pointer release() noexcept { return _M_t.release(); }
//reset接口
void reset(pointer __p = pointer()) noexcept {
_M_t.reset(std::move(__p));
}
//swap接口
void swap(unique_ptr& __u) noexcept {
_M_t.swap(__u._M_t);
}
// 禁用从lvalue拷贝操作.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
};
代码示例1
默认构造函数,操作符(), 以及get(),操作符->
#include <iostream>
#include<memory>
using namespace std;
class Car {
public:
Car() {
cout << "consutrct car" << endl;
}
virtual ~Car() {
cout << "desconsturct car" << endl;
}
void ShowInfo() {
cout << __func__ << ": this is base Car class" << endl;
}
void ShowInfoWithMember() {
cout << __func__ << ": var_test_:" << var_test_;
}
private:
int var_test_ = 0;
};
int main() {
std::unique_ptr<Car> aa;
if (!aa) {
cout << "aa is not nullptr" << endl;
}
if (!aa.get()) {
cout << "aa.get() is not nullptr" << endl;
}
aa->ShowInfo();
aa->ShowInfoWithMember()
}
示例运行结果:
过程分析:
那么该示例实际都调用了unique_ptr这个类中的哪些接口呢 ?
默认构造函数
std::unique_ptr<Car> aa;
上述语句会调用unique_ptr的默认构造函数:
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr() noexcept : _M_t() { }
可以看到class unique_ptr中有个成员_M_t:
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
..............;
__uniq_ptr_data<_Tp, _Dp> _M_t;
..............;
};
template <typename _Tp, typename _Dp>
struct __uniq_ptr_data<_Tp, _Dp, true, false> : __uniq_ptr_impl<_Tp, _Dp>
{
using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
__uniq_ptr_data(__uniq_ptr_data&&) = default;
__uniq_ptr_data& operator=(__uniq_ptr_data&&) = delete;
};
template <typename _Tp, typename _Dp>
class __uniq_ptr_impl
{
template <typename _Up, typename _Ep, typename = void>
struct _Ptr {
using type = _Up*;
};
template <typename _Up, typename _Ep>
struct _Ptr<_Up, _Ep, __void_t<typename remove_reference<_Ep>::type::pointer>>
{
using type = typename remove_reference<_Ep>::type::pointer;
};
public:
using _DeleterConstraint =
enable_if<__and_<__not_<is_pointer<_Dp>>,is_default_constructible<_Dp>>::value>;
using pointer = typename _Ptr<_Tp, _Dp>::type;
__uniq_ptr_impl() = default;
__uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
template<typename _Del>
__uniq_ptr_impl(pointer __p, _Del&& __d) : _M_t(__p, std::forward<_Del>(__d)) { }
__uniq_ptr_impl(__uniq_ptr_impl&& __u) noexcept: _M_t(std::move(__u._M_t))
{ __u._M_ptr() = nullptr; }
__uniq_ptr_impl& operator=(__uniq_ptr_impl&& __u) noexcept {
reset(__u.release());
_M_deleter() = std::forward<_Dp>(__u._M_deleter());
return *this;
}
pointer& _M_ptr() noexcept { return std::get<0>(_M_t); }
pointer _M_ptr() const noexcept { return std::get<0>(_M_t); }
_Dp& _M_deleter() noexcept { return std::get<1>(_M_t); }
const _Dp& _M_deleter() const noexcept { return std::get<1>(_M_t); }
void reset(pointer __p) noexcept {
const pointer __old_p = _M_ptr();
_M_ptr() = __p;
if (__old_p)
_M_deleter()(__old_p);
}
pointer release() noexcept {
pointer __p = _M_ptr();
_M_ptr() = nullptr;
return __p;
}
void swap(__uniq_ptr_impl& __rhs) noexcept
{
using std::swap;
swap(this->_M_ptr(), __rhs._M_ptr());
swap(this->_M_deleter(), __rhs._M_deleter());
}
private:
tuple<pointer, _Dp> _M_t;
};
通过上述代码可以看出unique_ptr() :: _M_t() 这个语句会调用到 __uniq_ptr_impl() = default; 而__uniq_ptr_impl中有一个成员:tuple<pointer, _Dp> _M_t;
操作符()
if (!aa.get()) {
cout << "aa.get() is nullptr" << endl;
}
上述语句会调用到unique_ptr::get()
pointer get() const noexcept
{ return _M_t._M_ptr(); }
再调用到__uniq_ptr_impl::_M_ptr()
pointer& _M_ptr() noexcept { return std::get<0>(_M_t); }
pointer _M_ptr() const noexcept { return std::get<0>(_M_t); }
最终取出的是tuple<pointer, _Dp> _M_t中的pointer这个指针,此例子中应该是Car* 类型,因为默认构造函数没有对tuple<pointer, _Dp> _M_t中的pointer这个指针赋值,所以unique_ptr::get()返回的就是空指针了。
操作符->
aa->ShowInfo();
上述代码实际上调用的是unique_ptr这个类中的操作符->
pointer operator->() const noexcept {
_GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
return get();
}
然而此测试代码中unique_ptr.get() 返回的是空,那为什么调用ShowInfo()函数不会crash ?
而调用ShowInfoWithMember() 这个接口就crash了呢 ?
void Car::ShowInfoWithMember() {
cout << __func__ << ": var_test_:" << var_test_;
}
aa->ShowInfoWithMember()
这篇博客就解释的很到位:C++---空指针能调用类成员函数吗?_shanghx_123的博客-CSDN博客_空指针调用成员函数
空指针为什么能调用成员函数?
对于类成员函数而言,并不是一个对象对应一个单独的成员函数体,而是此类的所有对象共用这个成员函数体。 当程序被编译之后,此成员函数地址即已确定。当调用p->func1(); 这句话时,其实就是调用A::func1(this);而成员函数的地址在编译时就已经确定, 需要注意的是,你用空指针调用成员函数,只是让this指针指向了空,所以空指针也是可以调用普通成员函数,只不过此时的this指针指向空而已,但函数fun1函数体内并没有用到this指针,所以不会出现问题。
当成员函数体内用到this指针时,如果你的this指针是空,那么程序就会崩溃。
代码示例2
reset() , release()函数
class Car {
public:
Car() {
cout << "consutrct car" << endl;
}
virtual ~Car() {
cout << "desconsturct car" << endl;
}
void ShowInfo(string ss) {
cout << __func__ << "," << ss << endl;
}
void ShowInfoWithMember(string ss) {
cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
}
private:
int var_test_ = 0;
};
auto DelCar = [] (Car *car) {
cout << "this is in DelCar function" << endl;
delete car;
};
int main() {
Car* pTest = new Car();
std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);
if (aa.get()) {
cout << "main: aa.get() is not nullptr" << endl;
} else {
cout << "main: aa.get() is nullptr" << endl;
}
aa->ShowInfo("aa");
aa->ShowInfoWithMember("aa");
std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
cout << "bb.reset" << endl;
bb.reset(pTest);
if (bb.get()) {
cout << "main: bb.get() is not nullptr" << endl;
} else {
cout << "main: bb.get() is nullptr" << endl;
}
if (aa.get()) {
cout << "main: aa.get() is not nullptr after reset bb" << endl;
} else {
cout << "main: aa.get() is nullptr after reset bb" << endl;
}
aa->ShowInfo("bb");
aa->ShowInfoWithMember("bb");
}
代码运行结果:
上述示例代码主要是示例带删除函数的unique_ptr的构造函数以及unique_ptr::reset()函数:
auto DelCar = [] (Car *car) {
cout << "this is in DelCar function" << endl;
delete car;
};
Car* pTest = new Car();
std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);
上面代码会调用的unique_ptr的构造函数:
template<typename _Del = deleter_type,
typename = _Require<is_copy_constructible<_Del>>>
unique_ptr(pointer __p, const deleter_type& __d) noexcept : _M_t(__p, __d) { }
reset函数
std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
cout << "bb.reset" << endl;
bb.reset(pTest);
上述代码会调用到unique_ptr的reset函数:
void reset(pointer __p = pointer()) noexcept {
_M_t.reset(std::move(__p));
}
然后会调用到__uniq_ptr_impl::reset
tuple<pointer, _Dp> _M_t;
pointer& _M_ptr() noexcept { return std::get<0>(_M_t); }
void reset(pointer __p) noexcept {
const pointer __old_p = _M_ptr();
_M_ptr() = __p;
if (__old_p)
_M_deleter()(__old_p);
}
通过__uniq_ptr_impl的reset函数源码可以知道,reset会先将old_ptr取出来,如果old_ptr不为空则向调用deleter所释放掉old_ptr, 然后将reset参数中新的ptr存入到tuple _M_t中。
使用pTest指针构造unique_ptr aa之后,再此使用pTest指针再构造一个unique_ptr bb会发生什么事情呢?
unique_ptr aa和bb 还是都在管理指针Car* pTest,并且都能正常访问pTest的成员函数和成员变量。只不过在aa析构的时候已经将pTest释放,而bb析构的时候再次释放pTest导致程序崩溃了。
release()
那么上述代码,如何不让它崩溃呢 ?使用unique_ptr::release()
int main() {
Car* pTest = new Car();
std::unique_ptr<Car, decltype(DelCar)> aa(pTest, DelCar);
......................;
aa->ShowInfo("aa");
aa->ShowInfoWithMember("aa");
std::unique_ptr<Car, decltype(DelCar)> bb(nullptr, DelCar);
cout << "bb.reset" << endl;
bb.reset(pTest);
.......................;
aa->ShowInfo("bb");
aa->ShowInfoWithMember("bb");
cout << "call aa.release()" << endl;
aa.release();
}
程序运行结果:
将aa release一把后,程序就不会蹦了。
下面来看看release函数的实现:
pointer __uniq_ptr_impl::release() noexcept {
pointer __p = _M_ptr();
_M_ptr() = nullptr;
return __p;
}
pointer unique_ptr::release() noexcept { return _M_t.release(); }
unique_ptr::release函数只是将unique_ptr中托管的指针置位nullptr,而不是像 unique_ptr::reset函数调用deleter将托管指针销毁。
unique_ptr拷贝构造函数和移动构造函数以及operator =
示例代码3
拷贝构造函数, operator =
class Car {
public:
Car() {
cout << "consutrct car" << endl;
}
virtual ~Car() {
cout << "desconsturct car" << endl;
}
void ShowInfo(string ss) {
cout << __func__ << "," << ss << endl;
}
void ShowInfoWithMember(string ss) {
cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
}
private:
int var_test_ = 0;
};
void func(std::unique_ptr<Car> pp) {
pp->ShowInfoWithMember("func_test");
}
unique_ptr<Car> func2(std::unique_ptr<Car> &pp) {
return pp;
}
int main() {
Car* pTest = new Car();
std::unique_ptr<Car> aa(pTest);
//执行拷贝构造函数三种场景
//1. 使用a对象初始化b对象
std::unique_ptr<Car> bb(aa);
//2. 函数参数
func(aa); //aa
//3. 返回值
unique_ptr cc = func2(aa);
//上面这三种情况都会因调用拷贝构造函数,而编译报错
std::unique_ptr<Car> bb(nullptr);
bb = aa; //会调用到operator = ,此处会编译报错
}
从unique_ptr的定义来看,拷贝构造函数和operator = 是被删除禁用的。所以会编译失败。
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
移动构造函数和移动operator =
上面的代码编译错误如何修复呢 ?
int main() {
Car* pTest = new Car();
std::unique_ptr<Car> aa(pTest);
// func(aa); //aa 作为函数参数,会调用到unique_ptr的拷贝构造函数,此时会编译报错
func(std::move(aa));
std::unique_ptr<Car> bb(nullptr);
//bb = aa; //会调用到operator = ,此处会编译报错
bb = std::move(aa);
}
使用std::move(aa)之后将aa变成右值,编译就不会报错了。此时会调用unique_ptr中的以下函数:
template<typename _Up,
typename _Ep,
typename = _Require<__safe_conversion_up<_Up, _Ep>,
__conditional_t<is_reference<_Dp>::value,
is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }
template<typename _Up, typename _Ep>
typename enable_if< __and_<__safe_conversion_up<_Up, _Ep>,
is_assignable<deleter_type&, _Ep&&>
>::value,unique_ptr&>::type
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
{
reset(__u.release());
get_deleter() = std::forward<_Ep>(__u.get_deleter());
return *this;
}
pointer __uniq_ptr_impl::release() noexcept {
pointer __p = _M_ptr();
_M_ptr() = nullptr;
return __p;
}
从上面的代码可以看出__u.release()将__u中托管的指针先取出来,然后置为nullptr,再return。也就是将托管的指针所有权进行了转移。
代码示例4
deleter
#include <iostream>
#include<memory>
using namespace std;
class Car;
auto DelCarLamda = [] (Car *car) {
cout << "this is in DelCar function" << endl;
delete car;
};
struct DelCarStruct {
void operator() (Car *car) const {
if (car != nullptr) {
delete car;
}
}
};
typedef void (*DelCarFuncType)(Car*);
void DelCarFunc(Car *car) {
if (car != nullptr) {
delete car;
}
}
int main() {
// need the second param: deleter var
std::unique_ptr<Car, decltype(DelCarLamda)> ll(new Car(), DelCarLamda);
cout << "use Lamda deleter, size:" << sizeof(ll) << endl;
// need the second param: deleter var
// 此种方式,unique_ptr的size会多一个指针变量的大小
std::unique_ptr<Car, DelCarFuncType> ff(new Car(), DelCarFunc);
cout << "use func pointer deleter, size:" << sizeof(ff) << endl;
// no need the second param
std::unique_ptr<Car, DelCarStruct> ss(new Car());
cout << "use struct operator () deleter, size:" << sizeof(ss) << endl;
}
class Car {
public:
Car() {
cout << "consutrct car" << endl;
}
virtual ~Car() {
cout << "desconsturct car" << endl;
}
void ShowInfo(string ss) {
cout << __func__ << "," << ss << endl;
}
void ShowInfoWithMember(string ss) {
cout << __func__ << "," << ss << ", var_test_:" << var_test_ << endl;
}
private:
int var_test_ = 0;
};
代码运行结果: