目录
为什么需要智能指针?
内存泄露
c++中没有内存回收,需要程序员自己使用delete,而智能指针就很好地解决了这个问题,可以自动正确地销毁动态创建的对象,防止内存泄漏
RALL
充分地利用了c++语言局部对象自动销毁的特性来控制资源(内存heap、网络套接字、互斥量,文件句柄等)的生命周期
RALL过程中的四个步骤
- 设计一个类封装资源
- 在构造函数中初始化
- 在析构函数中执行销毁操作
- 使用时定义一个该类的局部对象
裸指针的问题
- 很难区分指针的指向是单个对象还是一组对象
- 使用完成后不好判断该不该销毁指针
- 销毁的方式不同,例如是delete还是fclose
- 是delete还是delete[ ]
- 由于是手动调用delete,万一多调用了或者少调用了,还是会出现内存泄漏问题
- 没有办法分辨指针是否处于悬挂状态,即delete了指针但是并没有让它指向nullptr
智能指针有哪些
auto_ptr,unique_ptr,shared_ptr,weak_ptr
unique_ptr(唯一性智能指针)
特点:
- 一个指针只能指向一个资源,即一块资源不能被两个指针指向
- unique_ptr具有唯一性(即特点一中所指的特性),所以不提供构造函数和左值构造函数重载。
- 提供移动构造和移动赋值函数
- 为了实现单个对象和一组对象的管理添加了删除器类型
- 在容器保存指针是安全的
- unique_ptr具有->和*运算符重载,因此它可以像普通指针一样使用
创建模式:
如何来创建这个unique_ptr呢?先来引入一个头文件
#include<memory>//内存管理
int main()
{
std::unique_ptr<int> ip(new int(10));//持有了堆区的资源
*ip = 100;
cout << *ip << endl;
}
unique_ptr不用delete释放空间,系统自动调用析构函数释放空间
资源都是new出来的(堆区资源)
注意:unique_ptr是释放堆区的资源!
class Int
{
private:
int value;
public:
Int(int x = 0) :value(x) { cout << "Create Int" << this << endl; }
~Int() { cout << "Destroy Int: " << this << endl; }
Int(const Int& it) :value(it.value) { cout << "Copy Create Int: " << this << endl; }
Int& operator=(const Int& it)
{
if (this != &it)
{
value = it.value;
}
cout << this << " operator=()" << &it << endl;
return *this;
}
//void SetValue(int x) { value = x; }
//int GetValue() const { return value; }
int& Value() { return value; }
const int& Value() const { return value; }
};
template<class _Ty>
class my_unique_ptr
{
private:
_Ty * ptr;
public:
//typedef _Ty element_type;
//typedef _Ty* pointer;
using element_type = _Ty;
using pointer = _Ty*;
public:
explicit my_unique_ptr(pointer p = nullptr) :ptr(p) {}
~my_unique_ptr()
{
if (nullptr != ptr)
{
reset();//释放资源
}
} // delete ptr ; free(ptr);
//pointer get(my_unique_ptr * const this)
pointer get() const
{
return this->ptr;
}
//_Ty & operator*(my_unique_ptr * const this)
_Ty & operator*() const
{
return *this->get(); // return get(this);
} // *ip = 100; operator*(&ip) = 100;
pointer operator->() const
{
return get();
}
pointer release()
{
pointer old = ptr;
ptr = nullptr;
return old;
}
void reset(pointer p = nullptr)
{
if (ptr != nullptr)
{
delete ptr;
}
ptr = p;
}
//void swap(const my_unique_ptr* const this, my_unique_ptr& other);
void swap(my_unique_ptr& other)
{
//this = nullptr; error
std::swap(this->ptr, other.ptr);
} // ip.swap(sp); // swap(&ip,sp);
};
void funa()
{
my_unique_ptr<Int> sp(new Int(10));
my_unique_ptr<Int> ip(new Int(20));
my_unique_ptr<Int> ap;
ap = ip;
}
定义唯一性智能指针
my_unique_ptr<Int> sp(new Int(10));
my_unique_ptr<Int> ip(new Int(20));
my_unique_ptr<Int> ap;
ap = ip;
出现错误
ap和ip指向同一个地址,但是一个资源只能由一个智能指针进行管理,而不能有多个智能指针管理一个资源。
唯一性智能指针管理资源
要使得unique_ptr指针具有通用性,例如在文件的打开、关闭中,文件资源的管理是不一样的,要想保证unique_ptr的通用性,对特殊类型的资源管理,就自己重写删除器的类型。
整个程序的构建都是为了通用!!!(出现在人工智能)
const的修饰作用
//pointer get(my_unique_ptr * const this)
pointer get() const
{
return this->ptr;
}
模板的特化
调用情况
总结:
unique_ptr相比较于裸指针的特性
- 可以解决悬空指针或多次删除被指向对象
- 解决资源泄露问题
- 通常用来确保指针寿命和其指向对象寿命一致
注:多学一点
bool强转
可以将变量变为bool类型,在if语句中可以判断
operator bool() const
{
return (ptr !=nullptr);
}
const构成重载:const修饰*p
void func(int *p){}
void func(const int* p){}
移动构造
my_unique_ptr(const my_unique_ptr&& x)
{
ptr = x.ptr;
x.ptr = nullptr;
}
拷贝构造和移动构造同时书写了,那么就会调用移动构造
new int (10)所做的四件事
1.计算10的大小 2.malloc 3.赋值 4.返回所在地址
new没有能力去构造常对象
is具有常性,不可以对is的指向进行修改。
const修饰*is,is的指向可以修改
this指针---为什么函数里面需要this指针?
- 创建对象类型有多个,方法共享只有一个,用this来调用函数方法
- 系统是面向过程编程,底层把面向对象编程变为面向过程的体系
类的成员函数都含有this指针,只有static,友元,全局没有this指针。
.和->的区别
. 的时候函数都是智能指针的方法
-> 的时候函数都是 类 类 型 的方法
创建对象
std::unique_ptr<Int> ip(new(10));
std::unique_ptr<Int> sp=new Int(100);
仿函数
struct ADD
{
int operator()(int a, int b) const//仿函数()
{
return a + b;
}
};
int main()
{
ADD add;
int x = add(12, 23);//调用仿函数
//标准c++没有三元仿函数
x = add.operator()(12, 23);//把括号当成函数名了
}
new和malloc的区别
- 失败处理方式不同,new会抛出异常,malloc不会
- new有构造函数予以赋值
- new有重载运算符
c++系统自动给出的函数有
- 缺省的构造
- 缺省的拷贝构造
- 缺省的析构
- 缺省的赋值
!!!以上四个必须有
delete和delete[ ]区别何在
int main()
{
Int* p=new Int(10);
Int*s =new Int[10];
delete p;
delete[]s;
return 0;
}
检查有没有析构函数
小tips
写代码判断是否相等时得把常量放在左边,变量放在右边
explict外部
内存泄漏的本质:丢失了内存地址
不能重载的运算符:?:、::、*.