气象服务网站建设,网站突然被降权,天津专业做网站的公司,锦州哪里做网站智能指针#xff08;1#xff09; 概念内存泄漏指针指针概念RAII使用裸指针存在的问题 智能指针使用分类unique#xff08;唯一性智能指针#xff09;介绍智能指针的仿写代码理解删除器 概念
内存泄漏
内存泄漏#xff1a;程序中已动态分配的堆内存由于某些原因而未释放… 智能指针1 概念内存泄漏指针指针概念RAII使用裸指针存在的问题 智能指针使用分类unique唯一性智能指针介绍智能指针的仿写代码理解删除器 概念
内存泄漏
内存泄漏程序中已动态分配的堆内存由于某些原因而未释放或无法释放导致程序运行速度减慢甚至崩溃。 内存泄漏分为两类
堆内存泄漏在堆区申请了资源结束时候没有将其堆区进行释放导致该内存无法再次被使用。或者说指向堆区内存的指针丢失资源泄漏通常指系统资源比如套接字文件描述符等因为这些在系统中都是有限制的如果创建了而不归还长时间就会耗尽资源导致其他程序不可用。例如创建文件文件描述符打开文件但不归还不关闭文件。
指针指针概念
在java和C#语言中都有自动的垃圾回收机制系统可以自动释放资源为此在这些语言中内存管理都不是问题。 C中没有 垃圾回收机制必须自动取释放资源否则就会出现内存泄漏因为在开发中内存泄漏很容易出现但是又必须耗费大量精力去寻找解决内存泄漏所以出现了智能指针。
int main() {int* ipnew int(10);if(ip!nullptr) {//.....}//因为代码处理复杂忘记释放return 0;
}如上面就会出现内存泄漏而解决内存泄漏最有效的方法就是指针指针。 智能指针智能指针就是一个对象智能指针对象中有一个指针该指针存储的动态动态船舰对象的地址用于生存期的控制能够确保指针指针在对象离开作用域是自动调用析构函数来销毁对象释放内存防止内存泄漏。使用了智能指针就不会担心内存泄漏的问题了也不需要手动释放内存了。
RAII
RAII是资源获取即初始化使局部对象来管理资源的技术称为资源获取即初始化。 改方法就是充分利用了C局部对象自动销毁的特性来控制资源的生命周期。 这些资源主要指操作系统中内存套接字等局部对象是指存储在栈中的对象其生命周期靠操作系统来管理无需人工介入。 RAII过程四个步骤
设计一个类封装资源。在构造函数中初始化。在析构函数中执行销毁操作。使用时定义一个类的局部对象。
class Object {int* ip;
public:Object(int val 0) {ip new int(val);cout create obj endl;}~Object() {delete ip;cout destory obj endl;}
};
void func() {Object obj(3);return;
}
int main() {func();return 0;
}在func函数中创建了obj对象obj也就是局部对象在函数结束时系统会自动释放销毁对象也就自动调用了obj对象的析构函数。为此我们在使用资源的时候在构造函数中进行初始化在析构函数中进行销毁。
使用裸指针存在的问题
难以区分指针指向的是单一对象还是一组对象使用完无法判断是否应该销毁对象因为无法判断是否拥有指向的对象。销毁之后仍然指向堆内存但是堆内存已经释放在已经确定了销毁指针的情况下也无法确定是用那个关键字来删除例如deletefopen等即使确定了销毁指针的方法也无法判断删除的对象是一组还是一个deletedelete[]上面问题就算都解决了也难以保证在代码所有执行路径中有且只有一次销毁指针。理论上没有办法来分辨一个指针是否处于悬挂状态。
智能指针使用
分类
C11提供了4钟智能指针auto_ptr,unique_ptr,shared_ptr和weak_ptr. 使用时需要引入头文件#include memory 因为auto_ptr时C98标准中的C98中没有右值引用也没有move和forward为此已经被C11所弃用所有此处不做过多介绍。 智能指针主要应用于多线程问题因为在多线程中自主控制释放极为复杂智能指针可以在线程结束时自动释放该对象的资源。
unique唯一性智能指针
介绍
我们以自定义的int类型指针为例
int main() {unique_ptrPtrInt pa(new PtrInt(10));pa-Print();pa-SetValue(200);pa-Print();return 0;
}我们创建了唯一性指针对象pa指向船舰的PtrInt对象可以通过指针来调用类的成员方法。并且会自动析构。 错误使用
my_unique_ptrPtrInt pa3(new PtrInt(3));//A
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));PtrInt p(1);my_unique_ptrPtrInt pa1(p);//BPtrInt* ip new PtrInt(2);my_unique_ptrPtrInt pa2(ip);//C
}A使用智能指针管理全局变量是没有意义的因为程序结束后指针才销毁都不如直接定义一个全局变量其会在程序结束时自动销毁。智能指针主要依赖局部对象来释放 B:对象本身就是在栈中而智能指针是处理堆区堆存所以是错误的。 C:该方法本质上不是错误的因为你用了裸指针如果发现用了指针还没释放就会释放两次导致程序崩溃。 而这个唯一性指针是怎么实现的呢
智能指针的仿写
templateclass _Tp
struct my_default_deleter
{void operator()(_Tp* ptr)const {delete ptr;}
};
templateclass _Tp
struct my_default_deleter_Tp[]
{void operator()(_Tp* ptr)const {delete []ptr;}
};
struct FileDeleter {void operator()(FILE* pf) const {fclose(pf);}};
templateclass _Tp,class _Dpmy_default_deleter_Tp
//templateclass _Tp,class _Dp
class my_unique_ptr//_Tp[],_Dp
{
public:using pointer _Tp*;using elemtype_type _Tp;using deleter_type _Dp;
private:_Tp* ptr;deleter_type mDeleter;
public:/*deleter_type get_deleter()const {return mDeleter;}*/my_unique_ptr(_Tp* p nullptr) :ptr(p) { cout create myuniqueptr endl; }//构造函数~my_unique_ptr() {//析构mDeleter(ptr);//mDeleter.operator()(ptr);//get_deleter()(ptr);ptr nullptr;cout deatory myuniqueptr endl;}my_unique_ptr(const my_unique_ptr) delete;//删除拷贝构造my_unique_ptr operator(const my_unique_ptr) delete;//删除左值赋值重载my_unique_ptr(my_unique_ptr right) :ptr(right.ptr) {//移动构造right.ptr nullptr;cout move create myuniqueptr endl;}my_unique_ptr operator(my_unique_ptr right) {//右值赋值重载if (this right) return *this;reset(right.release());//首先返回参数对象的资源指针然后用该指针指向的资源代替现有指针的资源return *this;}pointer get() const { return ptr; }//返回指针_Tp operator*()const { return *get(); }//指针解引用pointer operator-()const { return get(); }//指向符号重载pointer release() {//资源返回将指向资源的指针返回pointer p ptr;ptr nullptr;return p;}void reset(pointer p nullptr) {//资源移动将形参指针指向的资源覆盖掉原本的资源释放原本资源。mDeleter(ptr);ptr p;}void swap(my_unique_ptr u) {//交换指针指向的资源std::swap(this-ptr, u.ptr);}operator bool() const {//判断是否指向有效空间return get() ! nullptr?true:false;}
};上面就是指针指针的仿写下面是使用 指向符重载解引用可以看出智能指针在这一块的使用和裸指针相同
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));(*pa).SetValue(100);(*pa).Print();pa-SetValue(200);pa-Print();//pa.operator-()-Print();此处系统省去了一个指向符。return 0;
}reset函数和release函数 release()返回指针 reset()资源覆盖释放原本资源
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));pa-Print();pa.reset(new PtrInt(100));//用新资源覆盖旧资源pa-Print();
}
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));PtrInt* p pa.get();//获取了指针delete p;//errorPtrInt* ip pa.release();//直接将指针指向nullptr并且将指向资源的指针返回。
}swap()函数智能交换具名对象
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));my_unique_ptrPtrInt pb(new PtrInt(20));pa-Print();pb-Print();pa.swap(pb);//只能和具名对象进行交换pa-Print();pb-Print();return 0;
}判空 判断指针是否为nullptr
int main() {unique_ptrPtrInt pa(new PtrInt(10));unique_ptrPtrInt pb(new PtrInt(20));my_unique_ptrPtrInt pc;if (pc) {//判断是否指向有效空间pc-Print();}
}移动拷贝移动赋值拷贝构造赋值重载 因为赋值重载和拷贝构造都是默认的浅拷贝所以会出现两个指针指向同一资源导致释放两次出现错误所以呢删除了系统默认的拷贝构造函数和赋值重载。 而移动构造和移动赋值是可以实现的。这也就是唯一性智能指针的特点有且只能管理一份资源。
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));//my_unique_ptrPtrInt pb(pa);//浅拷贝 errormy_unique_ptrPtrInt pb(new PtrInt(20));//pa pb;//浅拷贝 errmy_unique_ptrPtrInt pc(std::move(pa));pa std::move(pb);return 0;
}代码理解
my_unique_ptrPtrInt func(int x) {my_unique_ptrPtrInt pa(new PtrInt(x));return pa;
}
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));pa func(100);return 0;
}首先在主函数中创建了只能指针对象然后在func函数中船舰了智能指针的另一个对象函数结束时使用移动构造来构建一个将亡值对象然后移动赋值给pa对象。
void func(my_unique_ptrPtrInt px) {
}
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));func(std::move(pa));pa-Print();return 0;
}该程序会崩掉因为把pa的资源移动到了px参数中不存在拷贝构造所以程序会因为pa指向为nullptr而崩掉。
void func(my_unique_ptrPtrInt px) {px-Print();px-SetValue(200);px-Print();px.release();//my_unique_ptrPtrInt pb(std::move(px));
}
int main() {my_unique_ptrPtrInt pa(new PtrInt(10));func(pa);pa-Print();//errorreturn 0;
}不能用移动语义接收智能指针可以通过引用来接收智能指针但不要在函数内部让让智能指针的资源移动走否则智能指针指向nullptr。唯一性指针智能控制一个对象的资源。
删除器
在上面代码仿写中仿写了删除器主要是为了使程序可用性更高用到了仿函数大家可以看一看以前的文章理解一下。
int main() {my_unique_ptrFILE,FileDeleter pfile(fopen(D:/tulun/ConsoleApplication6/ConsoleApplication6.cpp, w));//D:\tulun\ConsoleApplication6\ConsoleApplication6.cppreturn 0;
}结果如下