- 问题引入:Java和C#等语言有自己的垃圾回收机制,.net运行时和java虚拟机可以管理分配的堆内存,在市区对象时自动回收,因此我们不需要去考虑垃圾回收问题,但是C++没有垃圾回收机制,我们必须要自己去释放分配的堆内存。
- 知识点:智能指针是指向动态分配(堆)对象指针,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每次使用它,内部的引用计数加1,每次析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。C++中的智能指针包括:
std::shared_ptr
std::unique_ptr
std::weak_ptr
- 怎么用:
1、智能指针std::shared_ptr的用法:通过构造函数、赋值函数、std::make_shared函数进行初始化,还可以指定删除器。智能指针的原始指针通过get()函数获取。
std::shared_ptr<int >p = (new int(2));
std::shared_ptr<int >p2 = p;
std::shared_ptr<Server> pServer= std::make_shared<Server>();
void DeleteIntPtr(int *p)
{
delete p;
}
std::shared_ptr<int >p = (new int,DeleteIntPtr);
std::shared_ptr<char>pBuffer = new char[len];
char* pchar = pBuffer.get();
注意事项:
A、不要把一个原生指针给多个智能指针管理:
int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error
导致ptr被删除两次
B、不要把this指针给shared_ptr:
C、不要在函数实参里创建shared_ptr ,function ( shared_ptr<int>(new int), g( ) ); 有缺陷可能的过程是先new int,然后调g( ),g( )发生异常,shared_ptr<int>没有创建,int内存泄露shared_ptr<int> p(new int()); f(p, g());
D、shared_ptr作为对象成员时,小心因循环引用而造成无法释放资源。
struct A;
struct B;
struct A
{
std::shared_ptr<B> m_b;
};
struct B
{
std::shared_ptr<A> m_a;
};
std::shared_ptr<A> ptrA(new A);
std::shared_ptr<B> ptrB(new B);
ptrA->m_b = ptrB;
ptrB->m_a = ptrA;
//ptrA和ptrB相互引用,离开作用域时引用计数都为1,导致内存没有被释放,
//解决办法是把A和B任何一个的成员变量改为weak_ptr
struct B
{
std::weak_ptr<A> m_a;
};
//ptrB->m_a不会增加A对象的引用计数,因此A对象离开作用域时,引
//用计数为0,m_b的引用计数减一,b离开作用域后引用计数由1减为0.
2、弱引用指针weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。没有重载 * 和 -> 但可以使用lock获得一个可用的shared_ptr对象,weak_ptr的使用更为复杂一点,它可以指向shared_ptr指针指向的对象内存,却并不拥有该内存,而使用weak_ptr成员lock,则可返回其指向内存的一个share_ptr对象,且在所指对象内存已经无效时,返回指针空值nullptr。
注意:weak_ptr并不拥有资源的所有权,所以不能直接使用资源。可以从一个weak_ptr构造一个shared_ptr以取得共享资源的所有权。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <map>
void check(std::weak_ptr<int> &wp)
{
std::shared_ptr<int> sp = wp.lock(); // 转换为shared_ptr<int>
if (sp != nullptr)
{
std::cout << "still: " << *sp << std::endl;
}
else
{
std::cout << "still: " << "pointer is invalid" << std::endl;
}
}
void mytest()
{
std::shared_ptr<int> sp1(new int(22));
std::shared_ptr<int> sp2 = sp1;
std::weak_ptr<int> wp = sp1; // 指向shared_ptr<int>所指对象
std::cout << "count: " << wp.use_count() << std::endl; // count: 2
std::cout << *sp1 << std::endl; // 22
std::cout << *sp2 << std::endl; // 22
check(wp); // still: 22
sp1.reset();
std::cout << "count: " << wp.use_count() << std::endl; // count: 1
std::cout << *sp2 << std::endl; // 22
check(wp); // still: 22
sp2.reset();
std::cout << "count: " << wp.use_count() << std::endl; // count: 0
check(wp); // still: pointer is invalid
return;
}
int main()
{
mytest();
system("pause");
return 0;
}
3、unique_ptr不会共享它的指针。 无法将它复制到另一个unique_ptr, unique_ptr只能移动。 这意味着内存资源的所有权将转移到新的unique_ptr和原始unique_ptr不再拥有它。
int* p = new int;
std::unique_ptr<int> ptr(p);
std::unique_ptr<int> ptr1 = ptr; //不能复制,编译报错
auto ptr2 = std::move(ptr); //转移所有权, 现在ptr那块内存归ptr2所有, ptr成为无效的指针.
4 、用模板实现的智能指针
//智能指针
// 1、复制对象时,副本和原对象都指向同一块存储区域
template<typename T>
class SmartPtr
{
public:
SmartPtr(const T * ptr)_ptr(ptr)
{
if(_ptr)
{
_count = new size_t(1);
}
else
{
_count = new size_t(0);
}
}
SmartPtr(const SmartPtr<T>& rhs)
{
_count = rhs._count;
(*_count)++;
_ptr = rhs._ptr;
}
//四部曲
//1、检查自我赋值,2,与过去断绝关系,3,指向新世界,4,返回*this
SmartPtr &operator = (cosnt SmartPtr<T>& rhs)
{
if(*this != rhs)
{
if((*_count)==0)
{
delete _count;
delete _ptr;
_count =NULL;
_ptr =NULL;
}
_count = rhs._count;
++(*_count);
_ptr=rhs._ptr;
}
return *this
}
T* operator -> ()
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
~SmartPtr()
{
(*_count)--;
if(*_count == 0)
{
delete _count;
delete _ptr;
_count =NULL;
_ptr =NULL;
}
}
private:
T* _ptr;
size_t* _count;//指向引用计数器的指针
}