为了更容易(同时也更安全)地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,最重要的区别是它负责自动释放所指向的对象。不需要考虑内存泄露。
shared_ptr
允许多个指针指向同一个对象;
unique_ptr
则独占所指向的对象;
标准库还定义了一个名为weak_ptr
的伴随类,它是一种弱引用,指向shared_ptr
所管理的对象。这三种类型都定义在memory
头文件中。
1. unique_ptr
一个unique_ptr
拥有它所指向的对象。
当我们定义一个unique_ptr
时,需要将其绑定到一个new
返回的指针上,初始化unique_ptr
必须采用直接初始化形式。
1 2 3 4 5 6 7 8 9
| #include <iostream> #include <memory> using namespace std;
int main(){ unique_ptr<int> unPtr1(new int(10)); cout << *unPtr1 << endl; return 0; }
|
输出结果:
10
unique_ptr
不支持普通的拷贝或赋值操作。
1 2 3
| unique_ptr<int> unPtr1(new int(10)); unique_ptr<int> unPtr2(unPtr1); unique_ptr<int> unPtr2 = unPtr1;
|
虽然我们不能拷贝或赋值unique_ptr
,但可以通过调用move
来转移所有权。
1 2 3
| unique_ptr<int> unPtr1(new int(10)); unique_ptr<int> unPtr2 = move(unPtr1); cout << "unptr2 = " << *unPtr2 << endl;
|
输出结果:
unptr2 = 10
需要注意的是,一旦你转移了指针的所有权,之前的所有者,也就是unPtr1
就变为了空指针。
C++
11标准库中默认实现了make_shared
,但是没有给出一个make_unique
的实现。
本例实现make_unique
。
技术要点:
- 使用模板函数重载,分别支持普通指针,变长数组,不支持定长数组
std::enable_if
关键字根据不同条件,调用不同模板
std::unique_ptr
能构造和析构数组
make_unique.h
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 28 29 30 31 32 33 34 35
| #ifndef _MAKE_UNIQUE_HPP_ #define _MAKE_UNIQUE_HPP_ #include <type_traits> #include <memory>
template <typename T> using Ele = typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T> >::type;
template <typename T> using Slice = typename std::enable_if<std::is_array<T>::value && std::extent<T>::value == 0, std::unique_ptr<T>>::type;
template <typename T> using Arr = typename std::enable_if<std::extent<T>::value != 0, void>::type;
template <typename T, typename ... Args> inline Ele<T> make_unique(Args && ... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }
template <typename T> inline Slice<T> make_unique(size_t size) { using U = typename std::remove_extent<T>::type; return std::unique_ptr<T>(new U[size]); }
template <typename T, typename ... Args> Arr<T> make_unique(Args &&...) = delete;
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <memory> #include "make_unique.h" using namespace std;
class myClass{ public: myClass(){ cout << "Constructor invoked \n"; } ~myClass(){ cout << "Destructor invoked \n"; } };
int main(){ unique_ptr<myClass> unPtr1 = make_unique<myClass>(); return 0; }
|
输出结果:
Constructor invoked
Destructor invoked
2. shared_ptr
共享指针可以被共享,可以被多个所有者共享。智能指针也是模版,当我们创建一个智能指针时,必须提供额外的信息------指针可以指向的类型。
2.1 如何创建shared_ptr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <memory> using namespace std;
class myClass{ public: myClass(){ cout << "Constructor invoked \n"; } ~myClass(){ cout << "Destructor invoked \n"; } };
int main(){ shared_ptr<myClass> shPtr1 = make_shared<myClass>(); return 0; }
|
共享指针有所有指向这个指针的引用次数。我们可以通过use_count
方法来查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <memory> using namespace std;
class myClass{ public: myClass(){ cout << "Constructor invoked \n"; } ~myClass(){ cout << "Destructor invoked \n"; } };
int main(){ shared_ptr<myClass> shPtr1 = make_shared<myClass>(); cout << "Shared count: " << shPtr1.use_count() << endl; return 0; }
|
输出结果为:
Constructor invoked
Shared count: 1
Destructor invoked
可以看出我们只有一个指向这个类的指针。所以结果为1
,我们再创建一个类共享指针。
1 2 3 4
| shared_ptr<myClass> shPtr1 = make_shared<myClass>(); cout << "Shared count: " << shPtr1.use_count() << endl; shared_ptr<myClass> shPtr2 = shPtr1; cout << "Shared count: " << shPtr1.use_count() << endl;
|
输出结果:
Constructor invoked
Shared count: 1
Shared count: 2
Destructor invoked
可以看出此时结果为2。当我们销毁一个指向这个类的指针时,引用次数减一,直到引用次数为0
,内存才会被释放。
1 2 3 4 5 6 7
| shared_ptr<myClass> shPtr1 = make_shared<myClass>(); cout << "Shared count: " << shPtr1.use_count() << endl; { shared_ptr<myClass> shPtr2 = shPtr1; cout << "Shared count: " << shPtr1.use_count() << endl; } cout << "Shared count: " << shPtr1.use_count() << endl;
|
输出结果为:
Shared count: 1
Shared count: 2
Shared count: 1
可以看到shPtr2
超出它的作用域之后就被销毁了,所以最后的引用次数为1
。
3. weak_ptr
弱引用weak_ptr
是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr
管理的对象。前面我们知道多个shared_ptr
指向一个特定的内存位置时,会导致引用次数的增加。而将一个weak_ptr
绑定到一个shared_ptr
不会改变share_ptr
的引用次数。一旦最后一个指向对象的shared_ptr
被销毁,对象就会被释放,即使weak_ptr
指向对象,对象也会被释。
1 2 3 4 5
| weak_ptr<int> wePtr1; { shared_ptr<int> shPtr1 = make_shared<int>(25); wePtr1 = shPtr1; }
|
weptr1
就被赋值为25