Умные указатели 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набор текста