独占指针,对象所有权只能被一个指针所拥有,无法被两个或两个以上同时拥有;
不能进行拷贝及赋值操作,只能直接初始化;
可以从函数中返回一个unique_ptr
;
unique_ptr
为数组提供了模板偏特化,因此unique_ptr
也可以指向数组;
c++14 提供了make_unique
来直接创建;
作为函数参数使用时,使用引用可避免所有权转移;
实现原理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| //定义
namespace std {
template <typename T, typename D = default_delete<T>>
class unique_ptr
{
public:
explicit unique_ptr(pointer p) noexcept;
~unique_ptr() noexcept;
T& operator*() const;
T* operator->() const noexcept;
unique_ptr(const unique_ptr &) = delete; //禁用复制构造函数
unique_ptr& operator=(const unique_ptr &) = delete; //禁用赋值函数
unique_ptr(unique_ptr &&) noexcept;
unique_ptr& operator=(unique_ptr &&) noexcept;
// ...
private:
pointer __ptr;
};
}
//c++14
template <typename T, typename... Ts>
std::unique_ptr<T> make_unique( Ts&&... params ) {
return std::unique_ptr<T>( new T( std::forward<Ts>(params)... ) );
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // 4种初始化方式
unique_ptr<int> up1(new int()); // 使用原始指针初始化
unique_ptr<int> up2;
up2.reset(up1.release()); // 使用reset初始化
unique_ptr<int> up3 = std::move(up2); // 通过move转移所有权
auto ptr = make_unique<std::string>("senlin"); // c++14使用make_unique
//容器中使用
vector<unique_ptr<int>> vec;
unique_ptr<int> p(new int(5));
vec.push_back(std::move(p));
//函数参数及返回
void fun1(unique_ptr<int> arg);
fun1(std::move(p)); //必须使用move将所有权转移
//使用引用,无需转移所有权
void fun2(unique_ptr<int> &arg);
fun2(p);
//函数返回值为右值,无需使用move转移所有权
unique_ptr<int> func3() {
return p;
}
//unique_ptr管理数组
unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
|
共享指针,对象所有权可以被多个共享指针同时拥有;
内部使用引用计数自动记录引用次数的多少,当引用次数为0时,对象自动销毁;
使用make_shared
创建;
可以使用一个new表达式返回的指针进行初始化;但是不能将一个new表达式返回的指针赋值给shared_ptr;
一旦将一个指针交由shared_ptr管理之后,就不要再通过普通指针访问这块内存;
可以通过reset方法重置指向另一个对象,此时原对象的引用计数减一;
可以定制一个deleter函数,用于在shared_ptr释放对象时调用;
在有通过this指针构建shared_ptr的情况下要继承std::enable_shared_from_this
;
shared_ptr的计数操作具有原子性, 多线程操作不同的shared_ptr进行操作是线程安全的;
shared_ptr本身就没有保证线程安全,多线程同时访问同一个shared_ptr对象线程不安全;
1
2
3
4
5
6
7
| cout<<"test shared_ptr and new:"<<endl;
shared_ptr<int> p4(new int(1024));
//shared_ptr<int> p5 = new int(1024); // wrong, no implicit constructor
cout<<*p4<<endl;
auto p2 = make_shared<string>("world");
cout<<*p1<<' '<<*p2<<endl;
|
如果涉及到将this指针提升为shared_ptr的情况,直接提升会新建一个manager object。
使用两个manager object管理同一个对象会造成不可预知的后果。为避免这种情况,需要在对象中维护一个weak_ptr。这是通过enable_shared_from_this自动完成的。
1
2
3
4
5
6
7
8
9
10
11
12
| void f(shared_ptr<Thing>);
class Thing {
public:
void foo() {
//f(shared_ptr<Thing>(this)); //new manager object A
f(shared_from_this()); //use manager object B
}
};
int main() {
shared_ptr<Thing> sp(new Thing()); //new manager object B
sp->foo();
}
|
当需要在object内部使用this指针时,调用shared_from_this()就可以避免新建manager object。需要注意的是,在构造函数中,对象还未构造完毕,并没有交由shared_ptr管理,即manager object还未创建,所以不能使用shared_from_this。
weak_ptr一般和shared_ptr配合使用,用于消除shared_ptr循环引用问题;
指向shared_ptr所指向的对象,但是却不增加对象的引用计数;
weak_ptr有一个lock函数,尝试取回一个指向对象的shared_ptr;
1
2
3
4
5
6
| auto p10 = make_shared<int>(1024);
weak_ptr<int> wp1(p10);
cout<<"p10 use_count: "<<p10.use_count()<<endl;
//p10.reset(new int(1025)); // this will cause wp1.lock() return a false obj
shared_ptr<int> p11 = wp1.lock();
if(p11) cout<<"wp1: "<<*p11<<" use count: "<<p11.use_count()<<endl;
|
必须保证所有managed object只有一个manager object;
能用裸指针解决问题的情况下,就不要使用智能指针;
如果决定了用智能指针,那就不要用裸指针管理同一个对象;
能用unique_ptr
管理的对象,不要使用shared_ptr
/weak_ptr
;
深入 C++ 的 unique_ptr | Senlin's Blog
https://zhuanlan.zhihu.com/p/30933682?utm_source=wechat_session&utm_medium=social&utm_oi=28398072102912