0%

C++:智能指针使用

智能指针

C++ 中的指针分为原始指针和智能指针
智能指针是裸指针的封装
智能指针只解决一部分问题:独占、共享所有权指针的释放、传输,没有从根本上解决C++内存安全的问题

独占指针:unique_ptr

特点:

  • 在任何时刻,只能有一个指针管理内存
  • 指针超出作用域,内存将自动释放
  • 该类型指针不可以copy,只可以move

三种创建方式

  • 通过已有裸指针创建
  • 通过new来创建
  • 通过std::make_unique创建(推荐)

unique_ptr可以通过get()来获取地址
unique_ptr实现了->*

  • 可以通过->调用成员函数
  • 可以通过*调用dereferencing解引用
    代码实例
    unique指针的三种创建方式
  1. 使用原始指针:注意将原始指针置空

    1
    2
    3
    4
    Cat *c_ptr=new Cat("aa");
    unique_ptr<Cat> u_c_ptr(c_ptr);
    //之后要将原始指针置空
    c_ptr=nullptr;
  2. 使用new

    1
    unique_ptr<Cat> u_ptr(new Cat("bb"));
  3. 使用make_unique(推荐)

    1
    unique_ptr<Cat> u_ptr=make_unique<Cat>("cc");

    程序结束会后自动调用析构函数!

其他:使用get()获取地址,*解引用

1
2
3
unique_ptr<int> u_ptr=make_unique<int>(10);
cout<<*u_ptr<<endl;
cout<<u_ptr.get()<<endl;
1
2
10
0x183fb0

unique_ptr与函数调用

  • unique_ptr是不可copy,只可以move
  • 作函数参数和函数返回值时要注意所有权
  1. passing by value
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void pass_by_value(unique_ptr<Cat> p){
    p->cat_info();
    }
    int main()
    {
    unique_ptr<Cat> c=make_unique<Cat>("Tom");
    pass_by_value(move(c));
    // 此时c已经被move到pass_by_value中,c已经不可用
    //c->cat_info();此时程序会崩溃
    pass_by_value(make_unique<Cat>("Jerry"));//默认调用move
    return 0;
    }
    1
    2
    3
    4
    5
    6
    Constructor of Cat:Tom
    cat info name:Tom
    Desctructor of Cat:Tom
    Constructor of Cat:Jerry
    cat info name:Jerry
    Desctructor of Cat:Jerry
  2. passing by reference
    • 不加const的情况
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      void pass_by_reference(unique_ptr<Cat> &p)
      {
      p->set_name("Amy");
      p->cat_info();
      p.reset();// 清空
      }
      int main()
      {
      unique_ptr<Cat> cat = make_unique<Cat>("Tom");
      pass_by_reference(cat);
      cout << "=============unique_ptr=============" << endl;
      // 此时cat被清空了
      cout << "cat address:" << cat.get() << endl;
      return 0;
      }
      1
      2
      3
      4
      5
      Constructor of Cat:Tom
      cat info name:Amy
      Desctructor of Cat:Amy
      =============unique_ptr=============
      cat address:0
    • 加const的情况
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      void pass_by_reference(const unique_ptr<Cat> &p)
      {
      p->set_name("Amy");
      p->cat_info();
      // p.reset();加了const以后不允许清空
      //但还是可以对对象进行修改
      }
      int main()
      {
      unique_ptr<Cat> cat = make_unique<Cat>("Tom");
      pass_by_reference(cat);
      cout << "=============unique_ptr=============" << endl;
      cout << "cat address:" << cat.get() << endl;
      return 0;
      }
      1
      2
      3
      4
      5
      Constructor of Cat:Tom
      cat info name:Amy
      =============unique_ptr=============
      cat address:0x1f3f60
      Desctructor of Cat:Amy
  3. return by value
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    unique_ptr<Cat> get_unique_ptr()
    {
    unique_ptr<Cat> p = make_unique<Cat>("Local cat");
    cout << "unique_ptr addreess:" << p.get() << endl;
    return p;
    }
    int main()
    {
    cout<<get_unique_ptr().get()<<endl;
    cout << "=============unique_ptr=============" << endl;
    return 0;
    }
    1
    2
    3
    4
    Constructor of Cat:Local cat
    unique_ptr addreess:0x1043f60
    0x1043f60
    Desctructor of Cat:Local cat

共享指针:shared_ptr

  • shared_ptr又称计数指针
  • unique_ptr不同的是它可以共享数据,可以copy
  • shared_ptr创建了一个计数器,与类对象的内存相关联;copy则计数器+1,销毁则计数器-1;api为use_count()
  1. 常量类型
    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
       // 常量类型
    // 还有一种定义方式
    shared_ptr<int> ptr(new int(10));

    shared_ptr<int> p1 = make_shared<int>(10);
    cout << "value:" << *p1 << endl;
    cout << "use count:" << p1.use_count() << endl;

    // 复制,引用计数+1
    shared_ptr<int> p2 = p1;
    shared_ptr<int> p3 = p1;
    cout << "use count:" << p1.use_count() << endl;
    cout << "use count:" << p2.use_count() << endl;
    cout << "use count:" << p3.use_count() << endl;

    // 修改值,另一个值也会改变,因为指向一个地方
    *p3 = 30;
    cout << "p1 value:" << *p1 << endl;
    cout << "p2 value:" << *p2 << endl;

    // 清空一个
    p3 = nullptr;
    cout << "use count:" << p1.use_count() << endl;
    cout << "use count:" << p2.use_count() << endl;
    cout << "use count:" << p3.use_count() << endl;
  2. 自定义类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    shared_ptr<Cat> cat1 = make_shared<Cat>();
    cout << "cat use count:" << cat1.use_count() << endl;
    shared_ptr<Cat> cat2 = cat1;
    shared_ptr<Cat> cat3 = cat2;
    cout << "cat use count:" << cat1.use_count() << endl;
    cout << "cat use count:" << cat2.use_count() << endl;
    cout << "cat use count:" << cat3.use_count() << endl;
    cat1.reset();
    cout << "cat use count:" << cat1.use_count() << endl;
    cout << "cat use count:" << cat2.use_count() << endl;
    cout << "cat use count:" << cat3.use_count() << endl;
    cout << "==================================" << endl;
    return 0;
    1
    2
    3
    4
    5
    6
    7
    8
    cat use count:3
    cat use count:3
    cat use count:3
    cat use count:0
    cat use count:2
    cat use count:2
    ==================================
    Desctructor of Cat:Mimi
    引用计数不为0,程序结束之后销毁
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    shared_ptr<Cat> cat1 = make_shared<Cat>();
    cout << "cat use count:" << cat1.use_count() << endl;
    shared_ptr<Cat> cat2 = cat1;
    shared_ptr<Cat> cat3 = cat2;
    cout << "cat use count:" << cat1.use_count() << endl;
    cout << "cat use count:" << cat2.use_count() << endl;
    cout << "cat use count:" << cat3.use_count() << endl;
    cat1.reset();
    cout << "cat use count:" << cat1.use_count() << endl;
    cout << "cat use count:" << cat2.use_count() << endl;
    cout << "cat use count:" << cat3.use_count() << endl;
    cat2.reset();
    cat3.reset();
    cout << "==================================" << endl;
    return 0;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    cat use count:1
    cat use count:3
    cat use count:3
    cat use count:3
    cat use count:0
    cat use count:2
    cat use count:2
    Desctructor of Cat:Mimi
    ==================================
    引用计数为0,就调用析构函数

shared_ptr和函数调用

  1. shared_ptr passed by value
    涉及copy,内部引用计数+1,但离开函数use_count-1
  2. shared_ptr passed by ref
  3. return by value
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
void pass_by_value(shared_ptr<Cat> cat){
cat->cat_info();
cat->set_name("cat2");
cout<<"func1 use count:"<<cat.use_count()<<endl;
}
void pass_by_ref(shared_ptr<Cat> &cat){
cat->cat_info();
cout<<"func2 use count:"<<cat.use_count()<<endl;
}
// 一般不用
shared_ptr<Cat> get_shared_ptr(){
shared_ptr<Cat> cat=make_shared<Cat>("local cat");
return cat;
}
int main()
{
shared_ptr<Cat> cat1=make_shared<Cat>("cat1");

pass_by_value(cat1);
cat1->cat_info();
cout << "main use count:" << cat1.use_count() << endl;

pass_by_ref(cat1);
cat1->cat_info();
cout << "main use count:" << cat1.use_count() << endl;

shared_ptr<Cat> loacl_cat = get_shared_ptr();
loacl_cat->cat_info();
cout<<"==============================="<<endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Constructor of Cat:cat1
cat info name:cat1
func1 use count:2
cat info name:cat2
main use count:1
cat info name:cat2
func2 use count:1 // 此时引用计数并没有+1
cat info name:cat2
main use count:1
Constructor of Cat:local cat
cat info name:local cat
===============================
Desctructor of Cat:local cat
Desctructor of Cat:cat2

shared_ptr与unique_ptr

  • 不能将shared_ptr转换为unique_ptr
  • unique_ptr可以转换为shared_ptr
    • 通过std::move
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unique_ptr<Cat> get_unique_ptr()
{
unique_ptr<Cat> cat = make_unique<Cat>("local cat");
return cat;
}
int main()
{
unique_ptr<Cat> cat1 = make_unique<Cat>("Amy");
// 通过move将unique_ptr转换为shared_ptr
shared_ptr<Cat> cat2 = move(cat1); // 此时cat1已经不能用了

cout << "cat2 use count:" << cat2.use_count() << endl;
// 函数返回值为unique_ptr,可以用shared_ptr接收
shared_ptr<Cat> cat3 = get_unique_ptr();
if (cat3) {
cat3->cat_info();
cout << "cat3 use count:" << cat3.use_count() << endl;
}

cout << "===============================" << endl;
return 0;
}

weak_ptr

  • weak_ptr并不拥有内存的所有权,所以不能调用->*解引用
  • shared_ptr类似,但不影响对象引用计数
  • 为什么需要weak_ptr:解决shared_ptr循环引用的问题
    • 例:Person1中有一个成员friend,指向另一个Person2,Person2->friend又指向另一个Person3或者指向Person1,最后每个的引用计数都不为0 —— 循环引用
    • 这里需要用一个不需要拥有所有权的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
// weak_ptr不能单独存在
shared_ptr<Cat> s_cat1=make_shared<Cat>("cat1");
weak_ptr<Cat> w_cat1=s_cat1;
// weak_ptr不影响引用计数
cout<<"w_cat1:"<<w_cat1.use_count()<<endl;
cout<<"s_cat1:"<<s_cat1.use_count()<<endl;
// weak_ptr不能使用->和*操作符
// w_cat1->cat_info();
shared_ptr<Cat> s_cat2 = w_cat1.lock();
cout<<"w_cat1:"<<w_cat1.use_count()<<endl;
cout<<"s_cat1:"<<s_cat1.use_count()<<endl;
cout<<"s_cat2:"<<s_cat2.use_count()<<endl;
cout << "===============================" << endl;
return 0;
}

循环依赖的问题

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <memory>
#include <string>
using namespace std;

class Cat {
public:
explicit Cat(string name);
Cat() = default;
~Cat();
void cat_info() const
{
cout << "cat info name:" << name << endl;
}
string get_name() const
{
return name;
}
void set_name(const string &name)
{
this->name = name;
}
void set_friend(shared_ptr<Cat> m_friend)
{
this->m_friend = m_friend;
}

private:
string name{"Mimi"};
shared_ptr<Cat> m_friend;
};

Cat::Cat(string name) : name(name)
{
cout << "Constructor of Cat:" << name << endl;
}

Cat::~Cat()
{
cout << "Desctructor of Cat:" << name << endl;
}

int main()
{
shared_ptr<Cat> cat1 = make_shared<Cat>("cat1");
shared_ptr<Cat> cat3 = make_shared<Cat>("cat3");
shared_ptr<Cat> cat4 = make_shared<Cat>("cat4");

cat3->set_friend(cat4);
cat4->set_friend(cat3);

cout << "===============================" << endl;
return 0;
}

1
2
3
4
5
Constructor of Cat:cat1
Constructor of Cat:cat3
Constructor of Cat:cat4
===============================
Desctructor of Cat:cat1

发现没有调用cat3和cat4的析构函数
解决方法:将成员变量数据shared_ptr<Cat> m_friend;改为weak_ptr<Cat> m_friend;
但是void set_friend(shared_ptr<Cat> m_friend)这里的不用改

1
2
3
4
5
6
7
Constructor of Cat:cat1
Constructor of Cat:cat3
Constructor of Cat:cat4
===============================
Desctructor of Cat:cat4
Desctructor of Cat:cat3
Desctructor of Cat:cat1

此时可以正常析构