北京建站软件,瓜子二手车网站开发,网络营销出来可以干什么工作,住建部关于epc总承包文件动态内存管理常出现的两种问题#xff1a;
1.忘记释放内存,造成内存泄漏
2.这块内存还有其他指针指向的情况下#xff0c;就释放了它#xff0c;会产生引用非法内存的指针#xff0c;例如
如果类中有属性指向堆区#xff0c;做赋值操作时会出现浅拷贝的问题 内存泄漏分…动态内存管理常出现的两种问题
1.忘记释放内存,造成内存泄漏
2.这块内存还有其他指针指向的情况下就释放了它会产生引用非法内存的指针例如
如果类中有属性指向堆区做赋值操作时会出现浅拷贝的问题 内存泄漏分类:
1.堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据需要分配,
(malloc/calloc/realloc) / free 从堆中分配/释放的一块内存 new/delete 从堆中分配/释放的一块内存new []/delete[] 从堆中分配/释放的一块内存
没有配套使用,就产生Heap leak
2.系统资源泄漏 指程序使用系统分配的资源,比如套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定
RAII [1]Resource Acquisition Is Initialization,也称为“资源获取就是初始化”是C语言的一种管理资源、避免泄漏的惯用法。C标准保证任何情况下已构造的对象最终会销毁即它的析构函数最终会被调用。简单的说RAII 的做法是使用一个对象在其构造时获取资源在对象生命期控制对资源的访问使之始终保持有效最后在对象析构的时候释放资源。百度百科
这种做法有两大好处:
不需要显示地释放资源对象所有的资源在其生命期内始终保持有效
智能指针的原理
RAII特性重载operator*和opertaor-具有像指针一样的行为
#include iostream
using namespace std;
/*动态内存管理经常会出现两种问题:1.忘记释放内存,造成内存泄漏2.尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针智能指针可以更加容易(更加安全)的使用动态内存.它的行为类似常规指针,重要的区别是它自动释放所指向的对象内存泄漏:因为疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制.不仅自己无法使用这段内存,而且也无法再将这段内存分配给其他程序,因而造成了内存的浪费内存泄漏危害:长期运行的程序出现内存泄漏,影响很不好.如操作系统/后台服务等出现内存泄漏会导致响应越来越慢,最终卡死,宕机内存泄漏分类:1.堆内存泄漏(Heap leak)堆内存指的是程序执行中依据需要分配,(malloc/calloc/realloc) / (free) 从堆中分配/释放的一块内存 (new)/(delete) 从堆中分配/释放的一块内存 假设程序设计错误导致这块内存没有释放掉, 这部分空间再也无法被使用,也就产生Heap leak2.系统资源泄漏指程序使用系统分配的资源,比方套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等等)的简单技术在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源.借此,我们实际上把管理一份资源的责任托管给了一个对象这种做法有两大好处:1.不需要显示地释放资源2.采用这种方式,对象所有的资源在其生命期内始终保持有效3.智能指针的原理*/
#include iostream
using namespace std;templateclass T
class SmartPtr {
public:SmartPtr(T* ptr) :m_ptr(ptr) {}~SmartPtr() {coutdelete-m_ptrendl;// 释放资源delete m_ptr;}//指针可以通过-去访问所指空间中的内容(-重载)T* operator-() {return m_ptr;}// 指针可以解引用(*重载)T operator*() {return *m_ptr;}
private:T* m_ptr;
};struct Person {string m_name;int m_age;
};int main() {// p在出作用域的前一刻会自动调用析构函数来释放资源// SmartPtrint p(new int); // delete-0x224d5a3d1c0// 对同一个资源进行了多次析构造成的SmartPtrPerson person(new Person);person-m_name Tom;person-m_age 18;coutname: (*person).m_name age: person-m_ageendl;// 默认的拷贝构造函数就属于浅拷贝SmartPtrPerson p1(person);// SmartPtrPerson p1 person;coutname: p1-m_name age: p1-m_ageendl;return 0;
} 这里的SmartPtrPerson p1(person);用到了默认拷贝构造函数
SmartPtrPerson p1 person;用到了默认operator
大致实现逻辑如下
SmartPtr(const SmartPtrT sp) :m_ptr(sp.m_ptr) {}
SmartPtrT operator(const SmartPtrT sp) {if (this ! sp) {m_ptr sp.m_ptr;}return *this;
}
故对同一个资源进行了多次析构会造成异常中断
一、auto_ptr
(1) 智能指针auto_ptr
auto_ptr_test.cpp
#include iostream
#include string
#include memory
using namespace std;
// C98版本的库中就提供了auto_ptr的智能指针
class Person {
public:Person(string name,int age):m_name(name),m_age(age){}~Person(){cout~Person()endl;}void setName(string name) {m_name name;}void setAge(int age) {m_age age;}void print() const{coutname: m_name, age: m_ageendl;}
private:string m_name;int m_age;
};int main() {std::auto_ptrPerson ptr(new Person(Tom,12));ptr-print();// auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了std::auto_ptrPerson copyPtr(ptr);copyPtr-print();copyPtr-setName(Jerry);copyPtr-setAge(8);copyPtr-print();// 这里ptr就悬空了// ptr-setName(heheda);return 0;
}
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
name: Tom, age: 12
name: Tom, age: 12
name: Jerry, age: 8
~Person()
PS D:\Work\c\bin auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了 (2) auto_ptr的实现原理管理权转移的思想
AutoPtr_main.cpp
#include iostream
using namespace std;class Person {
public:Person(){ cout Person() endl; }~Person(){ cout ~Person() endl; }string m_name;int m_age;
};// 管理权转移的思想
templateclass T
class AutoPtr {
public:AutoPtr(T* ptr nullptr) : m_ptr(ptr) {}~AutoPtr() { if(m_ptr) delete m_ptr; }// 一旦发生拷贝,就把ap中资源转移到当前对象中,然后// 让ap与其所管理资源断开联系这样就解决了一块空// 间被多个对象使用而造成程序崩溃问题AutoPtr(AutoPtrT ap) : m_ptr(ap.m_ptr) { ap.m_ptr nullptr; }AutoPtrT operator(AutoPtrT ap) {// 检测是否为自己给自己赋值if (this ! ap) {// 释放当前对象中资源if(m_ptr) delete m_ptr;// 转移ap中资源到当前对象中m_ptr ap.m_ptr;ap.m_ptr nullptr;}return *this;}/*重载operator*和opertaor-具有像指针一样的行为*/T* operator-() const { return m_ptr; }T operator*() const { return *m_ptr; }
private:T* m_ptr;
};int main() {AutoPtrPerson ap(new Person);// 现在再从实现原理层来分析会发现,这里拷贝后把ap对象的指针赋空了// 导致ap对象悬空,再通过ap对象访问资源时就会出现问题AutoPtrPerson copy(ap);// 这里访问资源时,就会出问题// ap-m_name Tom;return 0;
}
用auto_ptr的原理来修改前面的Smart_Ptr
修改拷贝构造函数和operator函数 SmartPtr(SmartPtrT sp) :m_ptr(sp.m_ptr) {sp.m_ptr nullptr;}SmartPtrT operator(SmartPtrT sp) {// 检测是否为自己给自己赋值if (this ! sp) {// 释放当前对象中资源if (m_ptr) delete m_ptr;// 转移sp中资源到当前对象中m_ptr sp.m_ptr;sp.m_ptr nullptr;}return *this;}
SmartPtr.cpp
#include iostream
using namespace std;templateclass T
class SmartPtr {
public:SmartPtr(T* ptr) :m_ptr(ptr) {}~SmartPtr() {cout delete- m_ptr endl;// 释放资源if(m_ptr) delete m_ptr;}SmartPtr(SmartPtrT sp) :m_ptr(sp.m_ptr) {sp.m_ptr nullptr;}SmartPtrT operator(SmartPtrT sp) {// 检测是否为自己给自己赋值if (this ! sp) {// 释放当前对象中资源if (m_ptr) delete m_ptr;// 转移sp中资源到当前对象中m_ptr sp.m_ptr;sp.m_ptr nullptr;}return *this;}//指针可以通过-去访问所指空间中的内容(-重载)T* operator-() {return m_ptr;}// 指针可以解引用(*重载)T operator*() {return *m_ptr;}
private:T* m_ptr;
};struct Person {string m_name;int m_age;
};int main() {SmartPtrPerson person(new Person);person-m_name Tom;person-m_age 18;cout name: (*person).m_name age: person-m_age endl;// 默认的拷贝构造函数就属于浅拷贝SmartPtrPerson p1 person;cout name: p1-m_name age: p1-m_age endl;// 导致person对象悬空,再通过person对象访问资源时就会出现问题//cout name: (*person).m_name age: person-m_age endl; // 异常中断return 0;
}
二、unique_ptr
(1) 智能指针unique_ptr
unique_ptr_test.cpp
// C11中开始提供更靠谱的unique_ptr
#include iostream
#include string
#include memory
using namespace std;
class Person
{
public:Person(string name, int age) :m_name(name), m_age(age) { cout name: name , age: age endl; }~Person() { cout ~Person() endl; }string m_name;int m_age;
};
int main() {std::unique_ptrPerson person(new Person(heheda, 12));// unique_ptr的设计思路非常的粗暴-防拷贝也就是不让拷贝和赋值//unique_ptrPerson copyPerson(person); // error// 下面的可以不看unique_ptrPerson person2 std::move(person);cout name: person2-m_name , age: person2-m_age endl;//cout name: person-m_name , age: person-m_age endl; // 异常return 0;
}
(2) unique_ptr的实现原理简单粗暴的防拷贝,就是将拷贝构造函数和operator函数设置为delete
UniquePtr_main.cpp
#include iostream
#include string
using namespace std;
class Person
{
public:Person(string name,int age):m_name(name),m_age(age){ coutname: name, age: ageendl;}~Person(){ cout ~Person() endl; }string m_name;int m_age;
};
// unique_ptr的实现原理:简单粗暴的防拷贝
templateclass T
class UniquePtr {
public:UniquePtr(T* ptr nullptr) : m_ptr(ptr) {}~UniquePtr() { if(m_ptr) delete m_ptr; }T operator*() const { return *m_ptr; }T* operator-() const { return m_ptr; }// C11防拷贝的方式:deleteUniquePtr(UniquePtrT const) delete;UniquePtrT operator(UniquePtrT const) delete;
private:T* m_ptr;
};int main() {UniquePtrPerson person(new Person(heheda,12));// unique_ptr的设计思路非常的粗暴-防拷贝也就是不让拷贝和赋值// UniquePtrPerson copyPerson(person); // errorreturn 0;
}
三、shared_ptr
(1) 智能指针 shared_ptr shared_ptr是一种智能指针smart pointer作用有如同指针但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数reference counting。
一旦最后一个这样的指针被销毁也就是一旦某个对象的引用计数变为0这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。百度百科
shared_ptr_test.cpp
// C11中开始提供更靠谱的并且支持拷贝的shared_ptr
#include iostream
#include string
#include memory
using namespace std;
class Person
{
public:Person(string name, int age) :m_name(name), m_age(age) { cout name: name , age: age endl; }~Person() { cout ~Person() endl; }string m_name;int m_age;
};
int main() {// shared_ptr 通过引用计数支持智能指针对象的拷贝shared_ptrPerson sp(new Person(heheda, 12));shared_ptrPerson copySp sp;cout ref count: sp.use_count() endl;cout ref count: copySp.use_count() endl;return 0;
} (2) shared_ptr的实现原理通过引用计数的方式来实现多个shared_ptr对象之间共享资源
1.类似RAII的思路普通构造创建资源、析构释放资源
2.像指针一样使用operator 、 operator-等运算符的重载 3.能够拷贝(且必须是浅拷贝) 自己编写拷贝构造、赋值重载的函数 4.计数器Count计数器必须是同一份资源的计数器而且是多个对象所共享的
不能是普通int型变量不然每个对象都会有不同的一个count也不能是static型变量否则所有资源都是共享的同一份计数器只能用int* 类型的计数器 5.SharedPtr本身是线程安全的所以在实现的过程中也必须上锁
#include iostream
#include string
#include mutex
using namespace std;class Person
{
public:Person(string name,int age):m_name(name),m_age(age){ coutname: name, age: ageendl;}~Person(){ cout ~Person() endl; }string getName() const { return m_name; }int getAge() const { return m_age; }
private:string m_name;int m_age;
};template class T
class SharePtr
{
public:SharePtr(T *ptr nullptr): m_ptr(ptr), m_pRefCount(new int(1)), m_mutex(new mutex){}// 拷贝构造函数SharePtr(const SharePtrT sp): m_ptr(sp.m_ptr), m_pRefCount(sp.m_pRefCount), m_mutex(sp.m_mutex){// 增加引用计数addRefCount();}// operatorSharePtrT operator(const SharePtrT sp) {// if(this ! sp)// 这样写防止 p2(p1) p1p2 防止自己赋值自己if(m_ptr ! sp.m_ptr) {// 先释放之前管理的资源release();m_ptr sp.m_ptr;m_pRefCount sp.m_pRefCount;m_mutex sp.m_mutex;// 增加引用计数addRefCount();}return *this;}T* get() { return m_ptr; }int useCount() { return *m_pRefCount; }// operator 、 operator-等运算符的重载T* operator-(){ return m_ptr; }T operator*(){ return *m_ptr; }~SharePtr() { release(); }
private:void release() {bool deleteflag false;m_mutex-lock();if(--(*m_pRefCount) 0) {cout delete: m_ptr endl;// 释放资源delete m_ptr;delete m_pRefCount;deleteflag true;}m_mutex-unlock();//表示m_ptr 和 m_pRefCount资源已经被释放此时需要释放锁否则产生资源泄漏if(deleteflag) delete m_mutex;}void addRefCount() {m_mutex-lock();(*m_pRefCount);m_mutex-unlock();}
private:T *m_ptr; // 指向管理资源的指针int *m_pRefCount; // 指向引用计数的指针mutex *m_mutex; // 互斥锁,处理线程安全
};int main() {// shared_ptr 通过引用计数支持智能指针对象的拷贝SharePtrPerson sp(new Person(heheda,12));SharePtrPerson copySp sp;cout ref count: sp.useCount() endl;cout ref count: copySp.useCount() endl;coutname:sp-getName() age:sp-getAge()endl;coutname:copySp-getName() age:copySp-getAge()endl;return 0;
}
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
name: heheda, age: 12
ref count:2
ref count:2
name:heheda age:12
name:heheda age:12
delete:0x21ef825f6a0
~Person()
PS D:\Work\c\bin
注意:智能指针是线程安全的,因为智能指针中引用计数,--加锁了。但是智能指针管理的资源不是线程安全的,需要自己手动控制
3循环引用
// std::shared_ptr的循环引用
#include iostream
#include string
#include memory
using namespace std;struct ListNode {int m_data;shared_ptrListNode m_prev;shared_ptrListNode m_next;~ListNode() { cout ~ListNode() endl; }
};int main() {shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);coutnode1.use_count() endl; coutnode2.use_count() endl;node1-m_next node2;node2-m_prev node1;coutnode1.use_count() endl;coutnode2.use_count() endl;// 循环引用:node1和node2在生命周期结束后并没有去调用析构函数return 0;
}
循环引用:node1和node2在生命周期结束后并没有去调用析构函数,原因就是引用计数不为0故导致资源无法释放
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
1
1
2
2
PS D:\Work\c\bin 四、weak_ptr
1weak_ptr_test.cpp
解决方案在引用计数的场景下把节点中的m_prev和m_next改成weak_ptr就可以了因为weak_ptr类型的不会自增引用计数
#include iostream
#include string
#include memory
using namespace std;// 解决方案在引用计数的场景下把节点中的m_prev和m_next改成weak_ptr就可以了
struct ListNode {int m_data;weak_ptrListNode m_prev;weak_ptrListNode m_next;~ListNode() { cout~ListNode()endl;}
};int main() {shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);coutnode1.use_count()endl; coutnode2.use_count()endl;node1-m_next node2;node2-m_prev node1;coutnode1.use_count()endl; coutnode2.use_count()endl;return 0;
}
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
1
1
1
1
~ListNode()
~ListNode()
PS D:\Work\c\bin
原因就是weak_ptr类型的不会使得引用计数自增 2weak_ptr的实现原理不让shared_ptr的引用计数增加就可以了
#include iostream
#include string
#include memory
using namespace std;templateclass T
class WeakPtr {
public:WeakPtr() : m_ptr(nullptr){}// 拷贝构造函数WeakPtr(const shared_ptrT sp) : m_ptr(sp.get()){}// operatorWeakPtr operator(const shared_ptrT sp) {m_ptr sp.get();return *this;}T operator*() { return *m_ptr; }// 重载*T* operator-() { return m_ptr; }// 重载-
private:T* m_ptr;
};struct ListNode {int m_data;WeakPtrListNode m_prev;WeakPtrListNode m_next;~ListNode() { cout~ListNode()endl;}
};// 原理:node1-m_next node2; 和node2-m_prev node1;
// 时weak_ptr的m_next 和 m_prev不会增加node1和node2的
// 引用计数
int main() {shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);coutnode1.use_count()endl; coutnode2.use_count()endl;node1-m_next node2;node2-m_prev node1;coutnode1.use_count()endl; coutnode2.use_count()endl;return 0;
}
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
1
1
1
1
~ListNode()
~ListNode()
PS D:\Work\c\bin 五、定制删除器
delete只会调用一次对象的析构函数而delete[]会调用后续所有对象的析构函数 // 定制删除器
#include iostream
#include memory
using namespace std;
class Object {
public:~Object() { cout ~Object() endl; }
private:int m_data 0;
};void test01() {unique_ptrObject p1(new Object);
}/*** 默认情况下:智能指针底层都是delete进行释放资源,* 这里new了10个Object,但底层delete时是delete p2* 而不是delete[] p2*
*/
void test02() {unique_ptrObject p2(new Object[10]); // error
}// 定制删除器
templateclass T
struct DeleteArray{void operator()(T* p) const {cout delete[] endl;delete[] p;}
};
// 如果资源是new[],malloc,fopen出来的,需要用到定制删除器
void test03() {unique_ptrObject, DeleteArrayObject p2(new Object[10]);
}int main() {test03();return 0;
}
PS D:\Work\c\bin .D:/Work/c/bin/app.exe
delete[]
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
PS D:\Work\c\bin 参考文章
C智能指针之shared_Ptr的原理以及简单实现_shared ptr-CSDN博客https://blog.csdn.net/Arthur__Cui/article/details/131969612
C中的浅拷贝、深拷贝、智能指针 - joannae - 博客园 (cnblogs.com)https://www.cnblogs.com/qionglouyuyu/p/4620714.html C智能指针_智能指针是线程安全的吗-CSDN博客https://blog.csdn.net/qq_56044032/article/details/125583967