智能指针
属于STL的智能指针,头文件<memory>
智能指针分类
unique_ptr 独享所有权(单个智能指针指向一块内存)shared_ptr 共享所有权(多个智能指针指向同一块内存)
unqiue_ptr
创建方式
语法 :
构建方法 | 说明 |
---|
std::unique_ptr<T> 智能指针变量名; | 创建空的智能指针 | std::unique_ptr<T> 智能指针变量名(new T); | 创建类型为T的智能指针 | std::unique_ptr<T> 智能指针变量名= std::make_unique<T>(); | 创建类型为T的智能指针(C++14) | 通过unique_ptr<T>::move() 转移获得 | 转移方法详情 |
#include <iostream>
#include <memory>
class Student
{
public:
Student(int ID) :m_ID(ID) {
std::cout << "学号" << this->m_ID << "被构造" << std::endl;
}
~Student() {
std::cout << "学号" << this->m_ID << "被销毁" << std::endl;
};
private:
int m_ID;
};
void func1() {
std::unique_ptr<Student> p1(new Student(7));
std::unique_ptr<Student> p2 = std::make_unique<Student>(8);
}
int main()
{
func1();
std::cout << "进入主函数" << std::endl;
return 0;
}
执行结果如下图所示:
使用智能指针之后,就可以不用关心delete 的时机,这些都由智能指针帮忙释放。
常用方法
方法 | 说明 | 范例 |
---|
方法1:ptr1 == nullptr 方法2:!ptr1 | 是否为空 | 范例1 | std::reset() | 重置:会delete所管理的指针 | 范例2 | std::release() | 释放:返回被管理的指针,智能指针置空 | 范例3 | std::move(智能指针) | 转移被管理的指针给其他unique_ptr,本身置空 | 范例4 | std::get() | 获取被管理的指针,智能指针仍在管理 | 范例5 |
范例1:是否为空
void func2() {
std::unique_ptr<int> ptr;
if (ptr == nullptr) {
std::cout << "方法1中 智能指针为空" << std::endl;
}
if (!ptr) {
std::cout << "方法2中 智能指针为空" << std::endl;
}
}
返回方法汇总表
范例2:重置智能指针
void func3() {
std::unique_ptr<int> ptr(new int(10));
ptr.reset();
if (ptr == nullptr) {
std::cout << "智能指针为空" << std::endl;
}
}
返回方法汇总表
范例3:释放智能指针
void func4() {
std::unique_ptr<int> ptr(new int(10));
int* p = ptr.release();
std::cout << "p指针的值为 " << *p << std::endl;
if (ptr == nullptr) {
std::cout << "释放后智能指针为空" << std::endl;
}
}
注意:不能使用智能指针来接受智能指针的释放
返回方法汇总表
范例4:转移智能指针的所有权
该方法就是可以解决范例3中的报错情况。
void func5() {
std::unique_ptr<int> ptr(new int(10));
std::unique_ptr<int> ptr2 = std::move(ptr);
if (ptr == nullptr) {
std::cout << "转移后的智能指针为空" << std::endl;
}
std::cout << "新的指针的值为 " << *ptr2 << std::endl;
}
返回方法汇总表
范例5:获取智能指针管理的指针
void func6() {
std::unique_ptr<int> ptr(new int(10));
int* p = ptr.get();
std::cout << "p指针的值为 " << *p << std::endl;
if (ptr == nullptr) {
std::cout << "获取后的智能指针为空" << std::endl;
}
else {
std::cout << "获取后的智能指针不为空,仍然有管理,值为" << *ptr <<std::endl;
}
}
注意:不能使用智能指针来接受智能指针的获取(智能指针在初始化时不能用其他智能指针的get()来初始化)
返回方法汇总表
类中包含智能指针的情况
class TESTPTR_B {
public:
TESTPTR_B() { std::cout << "==========TESTPTR_B 被创造==========" << std::endl; }
~TESTPTR_B() { std::cout << "==========TESTPTR_B 被析构==========" << std::endl; }
};
class TESTPTR
{
public:
TESTPTR() :iprt(new TESTPTR_B) { std::cout << "TESTPTR 被创造" << std::endl; }
~TESTPTR() { std::cout << "TESTPTR 被析构" << std::endl;}
private:
std::unique_ptr<TESTPTR_B> iprt;
};
int main()
{
TESTPTR a;
return 0;
}
不难看出,当TESTPTR对象a被exe回收的时候,也会把TESTPTR_B的对象析构,防止内存泄露。
注意:智能指针unqiue_ptr中途中被get()后,被管理指针(iprt)在其他地方被delete,那么在unqiue_ptr就需要release被管理的指针(iprt),不然unqiue_ptr在被回收时会再次delete 被管理的指针(iprt)从而造成程序崩溃。
class TESTPTR_B {
public:
TESTPTR_B() { std::cout << "==========TESTPTR_B 被创造==========" << std::endl; }
~TESTPTR_B() { std::cout << "==========TESTPTR_B 被析构==========" << std::endl; }
};
class TESTPTR
{
public:
TESTPTR() :iprt(new TESTPTR_B) { std::cout << "TESTPTR 被创造" << std::endl; }
~TESTPTR() { std::cout << "TESTPTR 被析构" << std::endl;}
void func8() {
TESTPTR_B *tmp = iprt.get();
delete tmp;
iprt.release();
}
private:
std::unique_ptr<TESTPTR_B> iprt;
};
int main()
{
TESTPTR a;
a.func8();
std::cout << "main函数结束" << std::endl;
return 0;
}
unqiue_ptr总结以及注意点
-
std::unique_ptr<T> Ptr = std::make_unique<T>(参数……) 该方法在C++14以后才被使用(含C++14) -
当指针交给智能指针管理之后,如果想要delete,就调用unqiue_ptr的reset就好 -
unqiue_ptr 不能通过拷贝构造生成,也不可用=运算符来赋值,只能通过移动来赋值。例子如下
shared_ptr
结构概述:
有两块数据组成,一个是数据块(存放指针),一个是计数块(记录引用计数)
创建方式
方法 | 说明 |
---|
std::shared_ptr<T> 智能指针变量名; | 创建空的智能指针 | std::shared_ptr<T> 智能指针变量名(new T); | 创建类型为T的智能指针 | std::shared_ptr<T> 智能指针变量名= std::make_shared<T>(); | 创建类型为T的智能指针(C++14) | std::shared_ptr<T> prt2(prt1) | prt2的指向 prt1所指向的内容 |
void func10() {
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2 = std::make_shared<int>(20);
std::shared_ptr<int> p3(p1);
std::shared_ptr<int> p4;
std::cout << "智能指针 p1 的值为" << *p1 << std::endl;
std::cout << "智能指针 p2 的值为" << *p2 << std::endl;
std::cout << "智能指针 p3 的值为" << *p3 << std::endl;
if (p4 != nullptr) {
std::cout << "智能指针 p4 的值为" << *p4 << std::endl;
}
else {
std::cout << "智能指针 p4 的值为空" << std::endl;
}
}
常用方法
方法 | 说明 | 范例 |
---|
方法1:ptr1 == nullptr 方法2:!ptr1 | 是否为空 | | use_count() | 返回被管理的指针的引用计数 | 范例1 | 智能指针=nullptr | 智能指针置空 | 范例2 | reset() | 智能指针重置 | 范例3 | reset(new <T>) | 智能指针重置后,重新管理新的指针 | 范例3 |
范例1:shared_ptr指针的引用计数
void func11() {
std::shared_ptr<int> p1;
std::cout << "空智能指针计数为 "<<p1.use_count() << std::endl << std::endl;
std::shared_ptr<int> p2(new int(10));
std::shared_ptr<int> p3(p2);
std::cout << "p2智能指针计数为 " << p2.use_count() << std::endl;
std::cout << "p3智能指针计数为 " << p3.use_count() << std::endl;
}
范例2:shared_ptr指针的置空
void func12() {
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);
std::cout << "p1智能指针计数为 " << p1.use_count() << std::endl;
p2 = nullptr;
std::cout << "p2智能指针置空后,p1智能指针计数为 " << p1.use_count() << std::endl;
if (p2 == nullptr) {
std::cout << "此时智能指针 p2 的值为空" << std::endl;
}
}
范例3:智能指针重置后,重新管理新的指针
void func13() {
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);
std::shared_ptr<int> p3(p1);
std::shared_ptr<int> p4(p1);
std::cout << "p1智能指针计数为 " << p1.use_count() << std::endl << std::endl;
p2.reset();
std::cout << "p2智能指针重置后p2计数为 " << p2.use_count() << std::endl;
std::cout << "此时p1智能指针计数为 " << p1.use_count() << std::endl << std::endl;
p3.reset(new int(20));
std::cout << "p3智能指针重置后,重新管理新的指针,此时p3计数为 " << p3.use_count() << std::endl;
std::cout << "此时p1智能指针计数为 " << p1.use_count() << std::endl << std::endl;
}
自定义删除器
智能指针在计数变为0的时候会调用析构函数的delete。但是管理数组指针的时候就有局限性,就需要使用自定义删除器。
主要有两种:
智能指针管理指针数组时
class MyClass
{
public:
MyClass() { std::cout << " 构造对象 " << std::endl; }
~MyClass() { std::cout << " 析构对象 " << std::endl; }
};
void self_delete(MyClass * ptr) {
delete[] ptr;
std::cout << " 删除数组指针 " << std::endl;
}
void func14() {
std::shared_ptr<MyClass[]> p1(new MyClass[5](), self_delete);
}
智能指针管理 含有指针的 STL容器
一般情况下,开发过程中如果要用到数组一般是使用STL的vector容器来代替数组,也可以交给智能指针管理。这里我用的是自定义数据类型。
void self_class_delete(std::vector<MyClass *> * ptr) {
std::cout << std::endl;
static int i = 0;
for (std::vector<MyClass *>::iterator it = ptr->begin(); it != ptr->end(); it++) {
std::cout << std::endl << "正在删除第"<< ++i << "个元素 *it的数据类型为 " << typeid(*it).name() << std::endl;
delete *it;
*it = nullptr;
if (it == ptr->end()) {
std::cout << " 到尾了 " << std::endl;
}
}
std::cout << " 删除完成 " << std::endl;
}
void func15() {
std::vector<MyClass *> * MyClassPtrVct = new std::vector<MyClass *>;
MyClassPtrVct->reserve(5);
MyClass *p1 = new MyClass;
MyClass *p2 = new MyClass;
MyClass *p3 = new MyClass;
std::cout << std::endl << " 指针加入vector容器 " << std::endl;
MyClassPtrVct->push_back(p1);
MyClassPtrVct->push_back(p2);
MyClassPtrVct->push_back(p3);
std::cout << std::endl << " vector容器交给shared_ptr智能指针管理 " << std::endl;
std::shared_ptr<std::vector<MyClass *>> AiPtr(MyClassPtrVct, self_class_delete);
AiPtr.reset();
}
使用函数对象作为自定义删除器
函数对象:自定义类型对()重载,也就是仿函数。注意仿函数和自定义的类要分开写,详情见下面遇到的问题。解决方法(正确写法)点此跳转
使用中遇到的问题:
主要遇到两种情况
- 智能指针存放自定义数据类型数组
- 智能指针存放自定义数据类型
问题描述:这两种情况使用自定义删除器,在创建智能指针的时候会额外构造对象!!虽然能正常释放……
eg1:智能指针中存放数组,数组存放自定义数据类型,使用函数对象作为自定义删除器来回收内存。
下面是错误写法
class DeleteTest {
public:
DeleteTest() {
static int i = 0;
this->m_ID = ++i;
std::cout << " DeleteTest " << this->m_ID << " 构造 " << std::endl;
}
~DeleteTest() {
std::cout << " DeleteTest " << this->m_ID << " 析构 " << std::endl;
}
void operator()(DeleteTest * ptr) {
std::cout << " DeleteTest 数组被删除 " << std::endl;
delete[] ptr;
}
private:
int m_ID;
};
void func16() {
std::shared_ptr<DeleteTest> p(new DeleteTest[3], DeleteTest());
std::cout << "创建结束" << std::endl;
p.reset();
std::cout << "释放智能指针所有权结束" << std::endl;
}
下图中红色方框都是问题所在
问题分析及解决方法
上述问题中主要的错误在于自定义数据类型和仿函数(函数对象)没有分开写,导致重载的()污染了原来的代码逻辑。
class Student {
public:
Student(){
static int i = 0;
this->m_ID = ++i;
std::cout <<"学生 "<< this->m_ID <<"被构造" << std::endl;
}
~Student() {
std::cout << "学生 " << this->m_ID << "被析构" << std::endl;
}
private:
int m_ID;
};
class CorrectDeleter{
public:
void operator()(Student* ptr) {
std::cout << " Student 数组被删除 " << std::endl;
delete[] ptr;
}
};
void func16_2() {
std::shared_ptr<Student> p(new Student[3], CorrectDeleter());
std::cout << "创建结束" << std::endl;
p.reset();
std::cout << "释放智能指针所有权结束" << std::endl;
}
使用C++11 lambda表达式作为删除器
lambda:lambda是C++11的新特性,可以代替 函数或者函数对象
class DeleteArrFunc
{
public:
DeleteArrFunc(int i):m_ID(i){
std::cout <<" DeleteArrFunc "<< this->m_ID <<" 被构造"<< std::endl;
}
~DeleteArrFunc() {
std::cout << " DeleteArrFunc " << this->m_ID <<" 析构 " << std::endl;
}
int m_ID;
};
void func17() {
std::shared_ptr<DeleteArrFunc> p(new DeleteArrFunc[3]{ 1,2,3 }, [](DeleteArrFunc * ptr) {
std::cout << " DeleteArrFunc 数组被删除 " << std::endl;
delete[] ptr;
});
std::cout << "结束" << std::endl;
p.reset();
std::cout << "释放智能指针所有权结束" << std::endl;
}
易错点:
1、不能使用栈中之中来构造智能指针
应为栈中的数据在超出作用域之后就会被程序回收,然后智能指针也会对这块数据
void func18()
{
int x = 12;
std::shared_ptr<int> ptr(&x);
}
需修改为
void func19()
{
int *x = new int(12);
std::shared_ptr<int> ptr(x);
}
2、不能用一个原始堆区数据来构造
int *num = new int(23);
std::shared_ptr<int> p1(num);
std::shared_ptr<int> p2(p1); // 正确使用方法
std::shared_ptr<int> p3(num); // 不推荐
std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 输出 2
std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 输出 2
std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 输出 1
参考文献
C++ 智能指针 shared_ptr 详解与示例 http://t.csdn.cn/jQGRL
C++ 智能指针 unique_ptr 详解与示例 http://t.csdn.cn/EuR0U
|