⌚前言
自C++11后,推出了三个智能指针。其中 unique_ptr
和shared_ptr
可以指定删除器。
但两者的形式却不太一样,本文将带你了解两者的基础使用区别。
⏲️注意
weak_ptr
是专门用来作为共享指针的使用权的,是一种零时的所有权,是没有删除器的。
make_xxx
是专门用于创建的,参数也很简单,就是直接转发到构造函数中,不附带指定删除器。(但不排除以后可能会添加兼容删除器的版本,但个人认为实现起来会比较麻烦)
本文不考虑数组版本的特化。
⌚unique_ptr
⏲️说明
类型声明
template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;
构造声明
unique_ptr( pointer p, ? d) noexcept;
unique_ptr( pointer p, ? d) = delete;
构造拥有 p 的 std::unique_ptr 对象,以 p 初始化存储的指针,并按下列方式初始化删除器 D(依赖于 D 是否为引用类型)。
a) 若 D 是非引用类型 A,则签名是:
unique_ptr(pointer p, const A& d) noexcept; // (1) (要求 Deleter 为不抛出可复制构造 (CopyConstructible) )
unique_ptr(pointer p, A&& d) noexcept; // (2) (要求 Deleter 为不抛出可移动构造 (MoveConstructible) )
b) 若 D 是左值引用类型 A&,则签名是:
unique_ptr(pointer p, A& d) noexcept;
unique_ptr(pointer p, A&& d) = delete;
c) 若 D 是左值引用类型 const A&,则签名是:
unique_ptr(pointer p, const A& d) noexcept;
unique_ptr(pointer p, const A&& d) = delete;
所有情况下删除器从 std::forward<decltype(d)>(d) 初始化。这些重载只有在 std::is_constructible<D, decltype(d)>::value 为 true 时才会参与重载决议。
说白了,就是模板的第二个删除器的参数类型要和构造函数的类型匹配。
比如,模板参数是左值引用A&
,那构造函数也必须是左值引用A&
。
⏲️实例
其实只要知道原理,熟悉模板和函数对象的朋友很容易能构造出实例。
基于仿函数
#include <iostream>
#include <memory>
struct MyDeleter {
void operator()(int* p) const {
std::cout << __func__ << std::endl;
delete p;
}
};
int main() {
MyDeleter del;
std::unique_ptr<int, MyDeleter> p0(new int{100}, del);
std::unique_ptr<int, MyDeleter> p1(new int{100}, MyDeleter{});
}
基于函数指针
#include <iostream>
#include <memory>
void del_fun(int* p) {
std::cout << __func__ << std::endl;
delete p;
}
int main() {
std::unique_ptr<int, void (*)(int*)> p0(new int(100), [](int* ptr) {
std::cout << __func__ << std::endl;
delete ptr;
});
std::unique_ptr<int, void (*)(int*)> p1(new int(100), del_fun);
std::unique_ptr<int, decltype(&del_fun)> p2(new int(100), del_fun);
}
删除器为引用类型
#include <iostream>
#include <memory>
struct MyDeleter {
// const 限定
void operator()(int* p) const {
std::cout << __func__ << std::endl;
delete p;
}
};
int main() {
//! 指定为非引用类型
std::unique_ptr<int, MyDeleter> p0(new int{100}, MyDeleter{});
//! 指定为左值引用
MyDeleter del;
std::unique_ptr<int, MyDeleter&> p1(new int{100}, del);
//! 指定类型为常引用
// 注意 unique_ptr(pointer p, const A&& d) = delete;
std::unique_ptr<int, const MyDeleter&> p2(new int{100}, del);
//! 指定为右值应用
// 错误,不能是右值引用
// std::unique_ptr<int, MyDeleter&&> p3(new int{100}, MyDeleter{});
}
⌚shared_ptr
⏲️说明
类型声明
template< class T > class shared_ptr;
构造声明
当然shared_ptr
的带有删除器的构造不止这一种,但是该构造是最具典型和功能单一的。
template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
以指定的删除器 d 为删除器。表达式 d(ptr)
必须良构,拥有良好定义行为且不抛异常。d 的构造和从它复制并存储的删除器的构造必须不抛异常。
Deleter
必须可复制构造 (CopyConstructible) 。(C++17 前)- 若表达式
d(ptr)
非良构,或若 std::is_move_constructible_v 为 false,则这些构造函数也不参与重载决议。 (C++17 起)
总之shared_ptr
的删除器不受类型干扰,而是在构造的时候确定,给了用户极大的自由度。
⏲️实例
可以方便的拷贝
#include <iostream>
#include <memory>
struct MyDeleter {
void operator()(int* p) const {
std::cout << __LINE__ << __func__ << std::endl;
delete p;
}
};
struct YourDeleter {
void operator()(int* p) const {
std::cout << __LINE__ << __func__ << std::endl;
delete p;
}
};
int main() {
MyDeleter my_del;
YourDeleter your_del;
std::shared_ptr<int> sp0(new int{100}, my_del);
std::cout << *sp0 << std::endl;
std::shared_ptr<int> sp1(new int{200}, your_del);
std::cout << *sp1 << std::endl;
// 类型是一样的,走拷贝赋值
sp0 = sp1;
}
可以方便的由容器管理
#include <iostream>
#include <memory>
#include <vector>
struct MyDeleter {
void operator()(int* p) const {
std::cout << __LINE__ << __func__ << std::endl;
delete p;
}
};
struct YourDeleter {
void operator()(int* p) const {
std::cout << __LINE__ << __func__ << std::endl;
delete p;
}
};
int main() {
std::shared_ptr<int> sp0(new int{100}, MyDeleter{});
std::shared_ptr<int> sp1(new int{200}, YourDeleter{});
// 统一使用vector进行管理
std::vector<std::shared_ptr<int>> sp_arr;
sp_arr.push_back(sp0);
sp_arr.push_back(sp1);
for (auto sp : sp_arr) {
std::cout << *sp << std::endl;
}
}
⌚拓展
⏲️函数类型 & 函数指针类型
#include <iostream>
int main() {
// 指针类型
using T0 = decltype(&main);
// 函数类型
using T1 = decltype(main);
// 函数指针
std::cout << typeid(&main).name() << std::endl;
// 函数类型
std::cout << typeid(main).name() << std::endl;
}
gcc mingw
PFivE
FivE
msvc
int (__cdecl*)(void)
int __cdecl(void)
⌚END
🌟视频讲解
🌟关注我
关注我,学习更多C/C++,算法,计算机知识
B站:
👨💻主页:天赐细莲 bilibili