智能指针的应用

智能指针解决的问题:一种是忘记释放内存形成泄露,另一种是尚有指针引用时就释放了它,产生引用非法内存的指针。
指针的作用:
1. 更好的管理内存
2. 实质是一个对象,行为表现像一个指针
3. 防止忘记用delete释放内存和程序异常的进入catch块忘记释放内存
4. 将普通的指针封装成一个栈对象,当栈对象的生存周期结束后,会在析构函数中释放申请的内存,防止泄露
原理:智能指针是一个类,超出类的实例对象的作用域时,会自动调用对象的析构函数,析构函数会自动释放内存。
Auto_ptr:原型
智能指针的应用_第1张图片

void testPointer::RunPtr2() {
    std::auto_ptr<TObj> obj(new TObj);//obj指向一个TObj
    obj->testData();
    std::auto_ptr<TObj> obj1 = obj;//转移指针
    obj1->testData();
    obj->testData(); //程序崩溃
}

复制auto_ptr对象时,把指针指传给复制出来的对象,原有对象的指针成员随后重置为nullptr。这说明auto_ptr是独占性的,不允许多个auto_ptr指向同一个资源。

Shared_ptr:资源可被多个指针共享,使用计数机制表明资源被几个指针共享,引用计数的内存在堆上分配,智能指针是个模板类,指定类型,传入指针通过构造函数初始化。**注意:不能将指针直接赋值给一个智能指针,智能指针是类,不要用一个原始指针初始化多个shared_ptr,会造成二次释放同一个内存,注意循环引用问题。 **
多个shared_ptr指向同一处资源,当所有shared_ptr都全部释放时,该处资源才释放。
(有某个对象的所有权(访问权,生命控制权) 即是强引用,所以shared_ptr是一种强引用型指针)

内部实现:
智能指针的应用_第2张图片

void testPointer::testSharedPointer() {
    std::shared_ptr<TObj> tobj(new TObj());//计数+1
    std::shared_ptr<TObj> tobj1 = tobj;//计数+2
    std::shared_ptr<TObj> tobj2 = tobj;//计数+3
    //栈退出后,shared_ptr释放 计数减为0 指向的堆被释放
}
void testPointer::testSharedPointer1() {
    std::shared_ptr<TObject> parent(new TObject());
    std::shared_ptr<TObject> child(new TObject());
    parent->setChild(child);
    child->setParent(parent);//栈退出时不执行析构函数
}

函数退出时栈的shared_ptr对象释放后的情形:
(1)一开始:father,son指向的堆对象 shared计数都是为2;
(2)son智能指针退出栈:son指向的堆对象 计数减为1,father指向的堆对象 计数仍为2。
(3)father智能指针退出栈:father指向的堆对象 计数减为1 , son指向的堆对象 计数仍为1。
(4)函数结束:所有计数都没有变0,也就是说中途没有释放任何堆对象。
为了解决这一缺陷的存在,弱引用指针weak_ptr的出现很有必要。
**shared_ptr自动销毁所管理的对象:**当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象,它是通过另一个特殊的成员函数-析构函数完成销毁工作的。析构函数一般用来释放对象所分配的资源。shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它所占用的内存。
**shared_ptr还会自动释放相关联的内存:**当动态对象不再被使用时,shared_ptr类还会自动释放动态对象,这一特性使得动态内存的使用变得非常容易。如果你将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。

尽量不要混合使用普通指针和智能指针
如果混合使用的话,智能指针自动释放之后,普通指针有时就会变成悬空指针,当将一个shared_ptr绑定到一个普通指针时,我们就将内存的管理责任交给了这个shared_ptr。一旦这样做了,我们就不应该再使用内置指针来访问shared_ptr所指向的内存了。也不要使用get初始化另一个智能指针或为智能指针赋值。
智能指针的应用_第3张图片
智能指针和异常:p是一个shared_ptr,因此p销毁时会检测引用计数,当发生异常时,我们直接管理的内存是不会自动释放的。如果使用内置指针管理内存,且在new之后在对应的delete之前发生了异常,则内存不会被释放。

Unique_ptr:同一时刻只能有一个unique_ptr指向指定对象,独占是最大特性
生命周期:指针创建开始直到离开作用域,离开时若其指向对象,将其所指向的对象销毁
使用std:move可将一个unique_ptr赋值给另一个
Boost库boost:scoped_ptr是独占性智能指针,不允许转移所有权

void testPointer::testUniquePtr() {
    std::unique_ptr<TObj> tobj(new TObj());
    //std::unique_ptr tobj1 = tobj;//error
    tobj->testData();//
}

不能拷贝或者赋值unique_ptr,但是可以通过调用release或reset将指针所有权从一个(非const)unique_ptr转移给另一个unique。

void testPointer::testUniquePtr1() {
    std::unique_ptr<TObj> tobj(new TObj());
    std::unique_ptr<TObj> tobj1(tobj.release());
    tobj1->testData();
}

Weak_ptr:shared_ptr存在内存泄漏的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会形成循环引用,使引用计数失败,导致内存泄漏。
Week_ptr协助shared_ptr工作,以旁观者观测资源使用情况,weak_ptr可以从一个shared_ptr或另一个weak_ptr对象构造获取观测权,没有共享资源,它的构造和析构不会引起引用计数的增加或减少。weak_ptr是为了辅助shared_ptr的存在,它只提供了对管理对象的一个访问手段,同时也可以实时动态地知道指向的对象是否存活。
(只有某个对象的访问权,而没有它的生命控制权 即是 弱引用,所以weak_ptr是一种弱引用型指针)
内部大概实现:
1.计数区域(SharedPtrControlBlock)结构体引进新的int变量weak_count,来作为弱引用计数。
2.每个weak_ptr都占指针的两倍空间,一个装着原始指针,一个装着计数区域的指针(和shared_ptr一样的成员)。
3.weak_ptr可以由一个shared_ptr或者另一个weak_ptr构造。
4.weak_ptr的构造和析构不会引起shared_count的增加或减少,只会引起weak_count的增加或减少。
被管理资源的释放只取决于shared计数,当shared计数为0,才会释放被管理资源,也就是说weak_ptr不控制资源的生命周期。但是计数区域的释放却取决于shared计数和weak计数,当两者均为0时,才会释放计数区域。
智能指针的应用_第4张图片
空悬指针问题是指:无法知道指针指向的堆内存是否已经释放。得益于引入的weak_count,weak_ptr指针可以使计数区域的生命周期受weak_ptr控制,从而能使weak_ptr获取 被管理资源的shared计数,从而判断被管理对象是否已被释放。(可以实时动态地知道指向的对象是否被释放,从而有效解决空悬指针问题)它的成员函数expired()就是判断指向的对象是否存活。

class TObject  : public QObject
{
	Q_OBJECT
public:
	TObject(QObject* parent = 0);
	~TObject();
	void setParent(std::shared_ptr<TObject> &parent);
	void setChild(std::shared_ptr<TObject> &child);
private:
	std::weak_ptr<TObject> m_parent;
	std::weak_ptr<TObject> m_child;
};
void testPointer::testSharedPointer1() {
    std::shared_ptr<TObject> parent(new TObject());
    std::shared_ptr<TObject> child(new TObject());
    parent->setChild(child);
    child->setParent(parent);//退出栈时析构函数被调用
}

可以说,当生命控制权没有彼此互相掌握时,才能正确解决循环引用问题,而弱引用的使用可以使生命控制权互相掌握的情况消失。
weak_ptr没有重载 * 和 -> ,所以并不能直接使用资源。但可以使用lock()获得一个可用的shared_ptr对象,如果对象已经死了,lock()会失败,返回一个空的shared_ptr。

void testPointer::testUniquePtr1() {
    std::shared_ptr<TObj> tobj(new TObj());
    std::weak_ptr<TObj> tobj1;
    if (auto observe = tobj1.lock()) {
        qDebug() << "0-----lock--------------------";
    }
    else {
        qDebug() << "0-----not lock--------------------";//执行
    }
    {
		tobj1 = tobj;
		if (auto observe = tobj1.lock()) {
			qDebug() << "1--------lock--------------------";//执行
		}
		else {
			qDebug() << "1--------not lock--------------------";
		}
    }
}

你可能感兴趣的:(Qt,qt)