C++11/14标准(二)智能指针

先说一句为什么要用智能指针。智能指针是为了解决内存泄露的问题。C/C++程序员可以自己开辟堆(heap)上的内存空间,同时也需要自己释放堆上的内存空间。一旦忘记释放内存空间,这样会造成内存泄露。

    不得不提及一下RAII机制(资源获取即初始化,Resource Acquisition Is Initialization)。在类的构造函数里面申请资源,然后使用,最后在析构函数中释放资源。所以这也就为什么析构函数需要用virtual来修饰来避免出现内存泄露。

    计算机有栈和堆两种。

    这里提及一点。C++中的对象(class)是指所有的内置型对象(int、float、double等和外置型(用户自定义的对象)对象。一切皆为对象。

如果在栈上创建相应的class,那么OS会自动释放掉相应的内存空间,所以RAII机制工作正常,离开相应的作用域时,class会自动调用自己的析构函数释放资源。但是如果采用new方式在堆上创建class,那么class不会调用自己的析构函数。程序员必须使用delete去销毁它。否则就会造成内存泄露。

    这里再插入两个小知识,我以前面试腾讯外包时被问到的点。

  1. new、delete的机制。

  2. new会抛出异常以及如何让new不抛异常。

    1.new有两个步骤:(1)调用operator new()函数开辟内存空间。(2)调用class的构造函数。

delete也有两个步骤:(1)调用class的析构函数。(2)调用operator delete()释放内存。

    2.new会抛出异常。为了防止抛出异常。就应该使用 std::nothrow。例:

扫描二维码关注公众号,回复: 11389445 查看本文章

 char *ptr = new(std::nothrow) char [20];

    开始介绍智能指针,他们都包含在头文件#include <memory>中。主要解决了部分获取资源自动释放的问题。

  • unique_ptr 

    独占式智能指针,不允许拷贝复制,不允许拷贝构造。它不仅能代理new创建的单个对象,也能够代理new[]创建的对象数组。

    代码如下:

 ///
 /// @file    unique.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-21 09:28:02
 ///
 
#include <iostream>
#include <memory>
#include <utility>
using std::cout;
using std::endl;

int main()
{
  //make_unique在C++11中被遗漏了,在C++14中添加。所以要使用-std=c++14
  
  //创建int型指针。
  //使用工厂方法创建
  std::unique_ptr<int> ptr = std::make_unique<int>(5);
  //也可以初始化时直接创建
  std::unique_ptr<int> ptr2(new int(6));
  
  //std::unique_ptr<int> ptr3(ptr); //不允许拷贝构造
  //std::unique_ptr<int> ptr3 = ptr;//不允许赋值
  
  //创建int型动态数组
  std::unique_ptr<int[]> ptrArray = std::make_unique<int[]>(20);

  cout << "*ptr: " << *ptr << endl;
  //创建出来的unique动态数组,可以使用下表访问。
  ptrArray[2] = 3;
  cout << ptrArray[2] <<  endl;

  //错误用法,能编译成功,但是会出问题。
  //std::unique_ptr<int> ptr5(new int[20]);

  return 0;
}
  • shared_ptr

    引用计数型智能指针。是最有价值、最重要、最有用的组成部分。可以自由地拷贝和赋值。当这个指针指向某个指针时,会引用计数加一。如果是从另外一个smart_ptr那里获取某指针的管理权,则两个smart_ptr指针都会引用计数加一。当引用计数为0时,smart_ptr会删除指针所指向的内存空间。

    代码如下:


 ///
 /// @file    shared.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-21 11:51:39
 ///
 
#include <iostream>
#include <memory>

using std::cout;
using std::endl;

int main()
{
  //初始化方式
  std::shared_ptr<int> ptr(new int(3));
  std::shared_ptr<int> ptr2 = std::make_shared<int>(5);

  //将ptr2管理的指针赋值给ptr3
  std::shared_ptr<int> ptr3(ptr2);
  cout << ptr3.use_count() << endl;
  return 0;
}

面试重点:循环引用计数

循环引用计数:两个smart_ptr指针互相指向对方,造成内存泄露。此时需要使用weak_ptr。

    代码如下:

  • weak_ptr

弱引用指针。用于观察shared_ptr或weak_ptr。用于解决循环引用计数。因为weak_ptr没有共享资源,它的构造函数不会引起指针引用计数的变化。

///
 /// @file    circle_reference.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-21 12:04:48
 ///
 
#include <iostream>
#include <memory>
using std::cout;
using std::endl;
using std::shared_ptr;

class Child;//前置声明
class Parent
{
public:
  Parent(){  cout << "Parent()" << endl;  }
  ~Parent() {  cout << "~Parent()" << endl;  }
  shared_ptr<Child> m_Child;
};

class Child
{
public:
  Child(){  cout << "Child()" << endl;  }
  ~Child(){  cout << "~Child()" << endl;  }
  shared_ptr<Parent> m_Parent;
};

int main(void)
{
  //shared_ptr的循环引用会导致内存泄漏
  shared_ptr<Parent> parent(new Parent);
  shared_ptr<Child> child(new Child);
  
  cout << "sizeof(shared_ptr) = " << sizeof(parent) << endl;
  cout << "parent 's use_count() = " << parent.use_count() << endl;
  cout << "child's use_count() = " << child.use_count() << endl;

  parent->m_Child = child;//赋值
  child->m_Parent = parent;
  cout << "parent 's use_count() = " << parent.use_count() << endl;
  cout << "child's use_count() = " << child.use_count() << endl;

  return 0;
}

使用weak_ptr后不会发生内存泄露。

 ///
 /// @file    circle_reference2.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-21 12:09:37
 ///
 
#include <iostream>
#include <memory>
using std::cout;
using std::endl;
using std::shared_ptr;
using std::weak_ptr;

class Child;//前置声明
class Parent
{
public:
  Parent(){  cout << "Parent()" << endl;  }
  ~Parent() {  cout << "~Parent()" << endl;  }
  shared_ptr<Child> m_Child;
};

class Child
{
public:
  Child(){  cout << "Child()" << endl;  }
  ~Child(){  cout << "~Child()" << endl;  }
  //shared_ptr<Parent> m_Parent;
  weak_ptr<Parent> m_Parent;
};

int main(void)
{
  //shared_ptr的循环引用会导致内存泄漏
  //
  //解决方案是将其中一个指针设置为weak_ptr
  //weak_ptr做赋值操作的时候,不会改变引用计数的值
  shared_ptr<Parent> parent(new Parent);
  shared_ptr<Child> child(new Child);
  cout << "sizeof(shared_ptr) = " << sizeof(parent) << endl;
  cout << "parent 's use_count() = " << parent.use_count() << endl;
  cout << "child's use_count() = " << child.use_count() << endl;

  parent->m_Child = child;//赋值
  child->m_Parent = parent;// weak_ptr = shared_ptr;
  cout << endl << "执行赋值之后:" << endl;
  cout << "parent 's use_count() = " << parent.use_count() << endl;
  cout << "child's use_count() = " << child.use_count() << endl;

  return 0;
}

 另一种循环引用计数:


closs node
{
public:
  typedef shared_ptr<Node> ptr_type;
  ptr_type next;
};

auto p1 = make_shared_ptr<Node>();
auto p2 = make_shared_ptr<Node>();

weak的lock函数,在某个作用于内使用lock()会返回一个shared_ptr。

该对象可以操作weak_ptr所指向的shared_ptr。

 ///
 /// @file    shared.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-21 11:51:39
 ///
 
#include <iostream>
#include <memory>

using std::cout;
using std::endl;

int main()
{
  //初始化方式
  std::shared_ptr<int> ptr(new int(3));
  std::shared_ptr<int> ptr2 = std::make_shared<int>(5);

  //将ptr2管理的指针赋值给ptr3
  std::shared_ptr<int> ptr3(ptr2);
  cout << "reference count: " <<  ptr3.use_count() << endl;

  std::weak_ptr<int> wptr(ptr);
  cout << "reference count: " <<  ptr3.use_count() << endl;
  {
    cout << "*ptr: " << *ptr << endl;
    std::weak_ptr<int> wptr(ptr);
    auto sp = wptr.lock();
    (*sp) = 5;
    cout << "*ptr: " << *ptr << endl;
  }
  cout << "*ptr: " << *ptr << endl;

  return 0;
}

猜你喜欢

转载自blog.csdn.net/koganlee/article/details/106910438
今日推荐