unique_ptr

使用make_unique创建一个unique_ptr:

1
unique_ptr<int>unPtr1 = make_unique<int>(25);

此时若运行:

1
cout << unPtr1 << endl;

会输出指针所指向内存的地址,所以如果我们想输出它的值(25)的话,就要运行:

1
cout << *unPtr1 << endl;

unique_ptr不能共享

如果这么写会报错:

1
2
unique_ptr<int>unPtr1 = make_unique<int>(25);
unique_ptr<int>unPtr2 = nPtr1;

唯一能做的就是更改内存地址的所有权,比如把所有权从unPtr1修改为unPtr2

1
unique_ptr<int>unPtr2 = move(unPtr1);

此时运行:

1
cout << *unPtr2 << endl;

会输出25

此时之前的指针unPtr1就会变成空指针,如果你再试图访问它的话会得到指针异常

1
cout << *unPtr1 << endl; //Exception Thrown

自动回收内存

所以不用再担心内存泄漏等情况

unique_ptr将在它的作用域结束时被销毁,我们可以用它新建一个类指针来进行实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
unique_ptr<MyClass>unPtr1 = make_unique<MyClass>();
}

此时只会输出Constructor invoked,因为该指针的作用域(最近的花括号)就是main函数,main函数已经结束了,所以看不到析构函数的调用。

如果我们把他修改成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
{
unique_ptr<MyClass>unPtr1 = make_unique<MyClass>();
}
}

此时Constructor invoked和Destructor invoked都会输出,证明析构函数被调用,内存得到释放。

shared_ptr

通过make_shared创建一个shared_ptr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
shared_ptr<MyClass>shPtr1 = make_shared<MyClass>();
}

use_count()

可以通过shPtr1.use_count()查看这个共享指针(内存位置)有多少个所有者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
shared_ptr<MyClass>shPtr1 = make_shared<MyClass>();
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
}

共享同一个内存位置

尝试再新建一个共享指针共享同一个内存位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
shared_ptr<MyClass>unPtr1 = make_shared<MyClass>();
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
shared_ptr<MyClass>unPtr2 = shPtr1;
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 2
}

我们也可以通过作用域测试来测试shared_ptr什么时候会被销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
shared_ptr<MyClass>unPtr1 = make_shared<MyClass>();
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
{
shared_ptr<MyClass>unPtr2 = shPtr1;
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 2
}
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
}

如果修改为这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Myclass {
public:
MyClass() {
cout << "Constructor invoked" << endl;
}
~MyClass() {
cout << "Destructor invoked" << endl;
}
};

int main()
{
{
shared_ptr<MyClass>unPtr1 = make_shared<MyClass>();
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
{
shared_ptr<MyClass>unPtr2 = shPtr1;
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 2
}
cout << "Shared count: " << shPtr1.use_count() << endl; //Shared count: 1
}
}

此时智能指针被自动销毁,输出为:

1
2
3
4
5
Constructor invoked
Shared count: 1
Shared count: 2
Shared count: 1
Destructor invoked

weak_ptr

共享指针与弱指针的区别:

弱指针被赋予跟其它指针同样的内存地址时,不会增加所有者的数量(即上文中的Shared count不变)

我们使用弱指针来在内存中定位一个特定的对象,但是如果没有其它需要的话,弱指针将不会使该对象保持活动,而共享指针将使该对象保持活动

1
2
3
4
5
6
7
8
int main()
{
weak_ptr<int> wePtr1;
{
shared_ptr<int>shPtr1 = make_shared<int>(25);
weakPtr1 = shPtr1;
}
}

首先我们必须清楚,这里的赋值方式有些不同,weak_ptr是在其下方作用域之外被定义的

此时我们通过断点来解释:

在第三行时,显示wePtr1为empty

在第五行时,该共享指针指向有一个强引用的内存位置,在这个内存位置上我们存储的值是25

在第六行时,显示弱指针也指向同样的存储的值为25的内存位置,此时该内存位置有一个强引用,一个弱引用

在第七行时,显示该内存位置只剩下一个弱引用,且弱指针显示为expired(过期)

此时,所有的该内存位置的所有者都被销毁了(由于作用域,此时共享指针已经被销毁),我们只剩下一个被分配的内存位置的地址。

也就是说,当该内存位置的最后一个强引用(即共享指针)被销毁时,它将被解除分配。

此时可能会有些迷糊啊,作为对比,如果我们把里面的弱指针也换成共享指针,那么结果就是依然有一个强引用存在与该内存位置,因为Ptr1是在其下方作用域之外被定义的