在介绍智能指针之前,我先来解释两个词:
①内存泄漏:由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete,比如流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见,并造成内存泄露。通俗的讲,是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。
内存泄漏的成因就不说了,因为上述概念就是成因
②指针悬挂:悬挂指针也叫野指针,是未初始化或未清零的指针。
悬挂指针的成因主要有两种:
- 指针变量没有被初始化
任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。 - 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。
1. 为什么要有智能指针?
- 就是为了解决内存泄漏和指针悬挂问题的。如此c++引入 智能指针 ,智能指针即是C++ RAII的一种应用,可用于动态资源管理,资源即对象的管理策略。 智能指针在 <memory>标头文件的 std 命名空间中定义。RAII 的主要原则是为所有堆分配资源提供所有权,以及任何相关清理代码。
2.智能指针的类别:
- c++ 智能指针主要包括:scope_ptr,shared_ptr, weak_ptr, 这三种,其中auto_ptr 已被遗弃,基本没人去使用。本着学习的原则,我接下来会逐一介绍四个指针。
- auto_ptr的主要思想是,控制权转移。
- scope_ptr(有的时候也称unique_ptr)的主要思想是,不允许拷贝,不允许赋值。
- share_ptr的主要思想是,采用引用计数。但是它带来一个循环引用的问题。
- weak_ptr是根share_ptr配套使用的,解决循环引用问题
auto_ptr.h
#pragma once
template<class T>class Auto_ptr
{
public:
//RAII的思想
Auto_ptr(T *ptr)
:_ptr(ptr)
{}
~Auto_ptr()
{
delete _ptr;//这个智能指针智能指向new出来的空间
_ptr = NULL;
}
//像普通指针一样
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
//(思想一:管理权转移)处理多个指针指向同一块空间问题
Auto_ptr(Auto_ptr<T>&ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
Auto_ptr<T>&operator=(Auto_ptr<T>&ap)
{
if (this != &ap)
{
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
private:
T* _ptr;
};
scope_ptr
#pragma once
template<class T>class Scoped_ptr
{
public:
//RAII的思想
Scoped_ptr(T*ptr)
:_ptr(ptr)
{}
~Scoped_ptr()
{
delete _ptr;//这个智能指针智能指向new出来的空间
_ptr = NULL;
}
//像普通指针一样
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
//(思想二:防拷贝,赋值)定义删除函数
Scoped_ptr(const Scoped_ptr<T>&ap) = delete;
Scoped_ptr<T>&operator=(const Scoped_ptr<T>&ap) = delete;
private:
(思想二:防拷贝,赋值)在私有下,只声明不定义
//Scoped_ptr(const Scoped_ptr<T>&ap);
//Scoped_ptr<T>&operator=(const Scoped_ptr<T>&ap);
private:
T* _ptr;
};
share_ptr
#pragma once
template<class T>class Weak_ptr;//前置声明
template<class T>class Shared
{
public:
friend Weak_ptr<T>;
//RAII的思想
Shared(T *ptr = NULL)
:_ptr(ptr)
, _refcount(new int(1))
{}
~Shared()
{
if (--(*_refcount) == 0)
{
delete _ptr;
delete _refcount;
}
_ptr = NULL;
_refcount = NULL;
}
//像普通指针一样
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
Shared(Shared<T>& ap)
:_ptr(ap._ptr)
, _refcount(ap._refcount)
{
++(*_refcount);
}
Shared<T>&operator=(Shared<T>&ap)
{
if (_ptr != ap._ptr)//判断两个对象是否指向同一块空间(防止给自己赋值)
{
if (--(*_refcount) == 0)
{ //①判断现在只想的空间是否还有其它指针指向②顺便把引用计数减一
delete _ptr;
delete _refcount;
_ptr = NULL;
_refcount = NULL;
}
_ptr = ap._ptr;
_refcount = ap._refcount;
++(*_refcount);
}
return *this;
}
private:
T *_ptr;
int *_refcount;
};
weak_ptr
#pragma once
//Weak_ptr只能与shared_ptr一起配套使用,解决循环引用问题
template<class T>
class Weak_ptr
{
public:
//RAII的思想
Weak_ptr(const Shared<T>&sp = Shared<T>())
:_ptr(sp._ptr)
{}
//像普通指针一样
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
Weak_ptr<T>&operator=(Shared<T>&ap)
{
_ptr = ap._ptr;
return *this;
}
private:
T *_ptr;
};
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<CoreWindow.h>
#include"auto_ptr.h"
#include"scped_ptr.h"
#include"shared_ptr.h"
#include"weak_ptr.h"
//所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放
//需要智能指针的目的:防止内存泄漏。
//智能指针的三大要素:①RAII思想(Resource Acquisition Is Initialization)
//资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源
//的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
//②能像普通指针一样运算
//③解决多个智能指针指向同一块空间的问题,防止该空间被释放多次
class AA
{
public:
int x;
};
void testAuto_ptr()
{
int a = 5;
Auto_ptr<int>ap1(new int(2));
*ap1 = 10;
Auto_ptr<AA> ap2 = new AA;
ap2->x = 10;
Auto_ptr<int>ap3(ap1);
Auto_ptr<int>ap4(new int(2));
ap4 = ap3;
if (1)
{
return;
}
}
void testScoped_ptr()
{
Scoped_ptr<int>ap1(new int(2));
*ap1 = 10;
Scoped_ptr<AA> ap2 = new AA;
ap2->x = 10;
if (1)
{
return;
}
}
void testShared_ptr()
{
Shared<int>ap1(new int(2));
*ap1 = 10;
Shared<AA> ap2 = new AA;
ap2->x = 10;
Shared<int>ap3(ap1);
Shared<int>ap4(new int(2));
ap4 = ap3;
if (1)
return;
}
//上面三个测试函数,分别测了三个指针
//------------------------------------分界线---------------------------------------//
//下面的两个测试函数,测试循环引用问题的
struct node
{
int _data;
Shared<node> _next;
node()
:_data(int())
, _next(Shared<node>())
{}
~node()
{
cout << "~node()" << endl;
}
};
void test()
{
Shared<node> next = new node;
{
Shared<node> cur = new node;
cur->_next = next;
next->_next = cur;//这句代码造成循环引用问题
}
cout << "hah";
}
struct node2
{
int _data;
Weak_ptr<node2> _next;
node2()
:_data(int())
, _next(Weak_ptr<node2>())
{}
~node2()
{
cout << "~node2()" << endl;
}
};
void test2()
{
Shared<node2> next = new node2;
{
Shared<node2> cur = new node2;
cur->_next = next;
next->_next = cur;//这句代码造成循环引用问题
}
cout << "hah";
}
int main()
{
//testAuto_ptr();
//testScoped_ptr();
//testShared_ptr();
//test();
//test2();
system("pause");
return 0;
}