C++基础智能指针之垃圾回收

  • 问题引入: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;//指向引用计数器的指针
}


                

猜你喜欢

转载自blog.csdn.net/wymtqq/article/details/79966285