Прочитав это, не говорите «нет умных указателей».

C++

Умные указатели C++

Во-первых, роль умных указателей

В предыдущей статье был представлен принцип и реализация пула памяти, подробнее см.Проектирование и реализация пула памяти;

Пул памяти может помочь нам эффективно управлять памятью, а интеллектуальные указатели могут легко управлять указателями, чтобы избежать утечек памяти;

Роль умных указателей

Роль умных указателей: умные указатели могут помочь нам избежать проблемы утечек памяти, вызванных забыванием об освобождении после подачи заявки на место; потому что умный указатель сам по себе является классом (он будет реализован сам по себе позже), когда он выходит за рамки класса , класс вызовет функцию деструктора для освобождения ресурсов. Таким образом, принцип интеллектуального указателя заключается в автоматическом освобождении памяти в конце функции, и нет необходимости вручную освобождать память.

Во-вторых, принцип умного указателя

Наши указатели здесь в основном относятся кshared_ptr, который также являетсяумный указатель с подсчетом ссылок, счетчик ссылок, как следует из названия, предназначен для записи количества интеллектуальных указателей, на которые имеются ссылки в памяти, добавления счетчика ссылок и добавления одного, а также уменьшения счетчика ссылок с истекшим сроком действия на 1. Когда счетчик ссылок равен 0,

Умные указатели освобождают ресурсы;

Дело номер один

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
 A()
 {
  cout << "A Constructor" << endl;
 }
 ~A()
 {
  cout << "A Destruct" << endl;
 }
};



int main()
{
 shared_ptr<A> p = make_shared<A>();
 cout << "count:"<<p.use_count() << endl;
 return 0;
}

результат:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test1 
A Constructor
count:1
A Destruct

Давайте добавим еще один параметр и посмотрим на счетчик ссылок:

Случай 2

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
 A()
 {
  cout << "A Constructor" << endl;
 }
 ~A()
 {
  cout << "A Destruct" << endl;
 }
};

void fun(shared_ptr<A>p)
{   
    cout<<"fun count:"<<p.use_count()<<endl;
}

int main()
{
 shared_ptr<A> p = make_shared<A>();
 cout << "count:"<<p.use_count() << endl;
    fun(p);
    cout << "count:"<<p.use_count() << endl;
 return 0;
}

результат:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test1 
A Constructor
count:1
fun count:2
count:1
A Destruct

С помощью приведенных выше двух примеров мы подтвердили то, что сказали в начале:Умный указатель сам по себе является классом (он будет реализован сам по себе позже), когда он выйдет за рамки класса, класс вызовет деструктор для освобождения ресурсов;

В-третьих, использование умных указателей

Использование интеллектуальных указателей относительно просто, и нам нужно включить файлы заголовков в нашу программу:

#include <memory>

Примечание. Интеллектуальные указателиC++11Стандарт нужно добавить при компиляции-std=c++11Параметры компиляции;

Существует несколько способов использования инициализации интеллектуального указателя.newа такжеmake_shared, рекомендуется использоватьmake_shared,Причина:make_sharedСтандартная библиотечная функция, которая является наиболее безопасным способом выделения и использования динамической памяти.Эта функция выделяет объект в динамической памяти и инициализирует его, а также возвращает указатель на этот объект.shared_ptr; файлы заголовков иshare_ptrтакой же.

Просто дайте случай:

Случай 3

#include <iostream>
#include <memory>

using namespace std;

class A
{
public:
    A(int count)
    {
        _nCount = count;
    }
    ~A(){}
    void Print()
    {
        cout<<"count:"<<_nCount<<endl;
    }
private:
    int _nCount;
};

int main()
{   
    shared_ptr<A>p = make_shared<A>(10);
    p->Print();
    return 0;
}

процесс компиляции;

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test2.cpp -o test2
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test2
count:10

4. Меры предосторожности при использовании интеллектуальных указателей

Давайте сначала посмотрим на кусок кода:

Случай 4

#include <iostream>
#include <memory>

using namespace std;

class B;

class A
{
public:
    shared_ptr<B>_pb;
};

class B
{
public:
    shared_ptr<A>_pa;
};

int main()
{
    shared_ptr<A>pa = make_shared<A>();
    shared_ptr<B>pb = make_shared<B>();
    cout<<"pa count:"<<pa.use_count()<<endl;
     cout<<"pb count:"<<pb.use_count()<<endl;
    pa->_pb = pb;
    pb->_pa = pa;
    cout<<"pa count:"<<pa.use_count()<<endl;
    cout<<"pb count:"<<pb.use_count()<<endl;
    return 0;
}

результат;

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test3.cpp -o test3
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test3
pa count:1
pb count:1
pa count:2
pb count:2

Будет обнаружено, что окончательный счетчик ссылок равен 2, затем, после окончания, счетчик ссылок не равен 0, и их место в куче не будет освобождено, о чем часто говорятциклическая ссылка;

Конечно, это не является неразрешимым, мы можем использовать другой вид единственного указателя, но это слабый указатель ---weak_ptr, этот тип указателя не будет увеличивать счетчик ссылок, сshared_ptr, можно сказать, талантливая женщина, все очень довольны!

Случай 5

#include <iostream>
#include <memory>

using namespace std;

class B;

class A
{
public:
    weak_ptr<B>_pb;
};

class B
{
public:
    weak_ptr<A>_pa;
};

int main()
{
    shared_ptr<A>pa = make_shared<A>();
    shared_ptr<B>pb = make_shared<B>();
    cout<<"pa count:"<<pa.use_count()<<endl;
     cout<<"pb count:"<<pb.use_count()<<endl;
    pa->_pb = pb;
    pb->_pa = pa;
    cout<<"pa count:"<<pa.use_count()<<endl;
    cout<<"pb count:"<<pb.use_count()<<endl;
    return 0;
}

результат:

root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# g++ -std=c++11 test3.cpp -o test3
root@iZuf67on1pthsuih96udyfZ:~/C++/Net_C++/demo4# ./test3
pa count:1
pb count:1
pa count:1
pb count:1

Наглядно обнаруживается, что счетчик ссылок не увеличивается на единицу при обращении друг к другу в конце, а деструктор вызывается для освобождения памяти, когда область видимости окончательно выходит из области видимости;

Пять, реализация интеллектуального указателя

Реализация интеллектуальных указателей очень полезна для нас как с точки зрения интервью, так и с точки зрения глубокого понимания интеллектуальных указателей.После понимания вышеуказанных принципов мы реализуем интеллектуальный указатель:

Код реализации умного указателя

#include <iostream>

using namespace std;

template<class T>
class SmartPoint
{
public:
 //构造函数
 SmartPoint(T* p=NULL)
 {
  _ptr = p;
  if (p != NULL)
  {
   _count = 1;
  }
  else
  {
   _count = 0;
  }
 }
 //析构函数
 ~SmartPoint()
 {
  if (--_count == 0)
  {
   delete _ptr;
  }
 }
 //拷贝构造函数
 SmartPoint(const SmartPoint& src)
 {
  if (this != &src)
  {
   _ptr = src._ptr;
   _count = src._count;
   _count++;
  }
 }

 //重载赋值操作符
 SmartPoint& operator=(const SmartPoint& src)
 {
  if (_ptr == src._ptr)
  {
   return *this;
  }
  if (_ptr)
  {
   _count--;
   if (_count == 0)
   {
    delete _ptr;
   }
  }
  _ptr = src._ptr;
  _count = src._count;
  _count++;
  return *this;
 }

 //重载操作符
 T* operator ->()
 {
  if (_ptr)
   return _ptr;
 }

 //重载操作符
 T& operator *()
 {
  if (_ptr)
   return *_ptr;
 }

 size_t use_count()
 {
  return _count;
 }
private:
 T* _ptr;
 size_t _count;

};

void Use(SmartPoint<char> p)
{
 int n = p.use_count();
}

int main()
{
 SmartPoint<char>sp1(new char);
 Use(sp1);
 SmartPoint<char>sp2(sp1);
 SmartPoint<char>sp3;
 sp3 = sp1;
 int n = sp1.use_count();
 return 0;
}

Рекомендуемые статьи в прошлом

Чтобы узнать больше о фоновых серверах C++, выполните следующие действия: Публичный аккаунт WeChat: ====**CPP后台服务器开发**====

В этой статье используетсяmdniceнабор текста