前言
c++程序执行时内存大致分为四个区域
代码区:存放函数体的二进制代码,由操作系统管理
全局区:存放全局变量和静态变量以及常量(const修饰的全局常量和字符串常量)
栈区:由编译器自动分配和释放,存放函数的参数和局部变量等,注意不要返回局部变量的地址,因为跳出作用域,局部变量就会被释放。局部常量存放在栈区
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
概要
c++需要程序员自己去管理内存的申请和释放,随着语言的进步,java、python使用了垃圾回收机制去管理内存,不再需要人为管理,相关的虚拟机会自动释放不需要的资源,显得十分智能。而c++采用的解决办法是RAII机制即资源获取即初始化,使用了引用计数的想法,让程序员不用担心内存释放或泄露问题。其核心原理是将引用计数、写时拷贝和类析构函数特性相结合。
c++11中主要有以下智能指针
auto_ptr:
1 可能会有两个auto指针对象拥有同一块堆内存,由于auto指针没有采用引用计数,在对象脱离作用域时,会自动析构释放内存,这样就使同一块堆内存释放两次,造成内存的重复释放;
2 两个智能指针对象在进行赋值的时候,内部指针的所有权会被转移,右侧的对象就不再具有此内部指针,这个时候再通过右侧智能指针对象访问内存就会报错。
#include#include #include using namespace std; void foo_test(auto_ptr p) { printf("%drn", *p); } int main() { auto_ptr p1 = auto_ptr (new int(3)); foo_test(p1); printf("%drn", *p1);//此处引发异常,因为foo_test处p1拷贝给p,使得p1实际已经丧失对堆内存的所有权 vector> ary; auto_ptr p2(new int(3)); ary.push_back(p2); printf("%drn", *p2);//此处引发异常,同样的道理,容器中存值也是拷贝过去 return 0; }
unique_ptr:它是对auto_ptr的一个语义明确,内部其实删除了拷贝构造函数和=运算符重载,使得只能采用move语义来移动,这样使得程序员明确知道被移动的对象已经没有内部指针了。
shared_ptr:浅拷贝:就是多个指针指向一个内存;深拷贝:就是重写开辟一个内存,将数据复制到内存中,然后将内存赋给左值对象。
此智能指针采用了引用计数计数,引用计数:对堆内存数据的引用次数用一个整数记录,当整数为0时就释放堆内存,这样就能做到自动释放内存的目地。但是它解决不了循环引用的问题,在循环引用时,不能做到内存的释放。循环引用就是俩个类互相有对方的shared_ptr指针,循环引用使得堆内存被引用计数了两次,而由类的析构引用计数只会减1变成1,它不为0堆内存不会释放。
上图是vs中自己实现的强指针为CStrongPtr,弱指针为 CMyWeakPtr等价于c++中的shared_ptr和weak_ptr,这里用自己实现的智能指针来展示循环引用。单步调试到释放之前由图可以得到两堆内存的m_usedCount都为2。
上图是跳出作用域,智能指针类析构后,由图可知m_usedCount还为1,堆内存并没有释放。
weak_ptr:弱指针的加入很好的解决了循环引用的问题,只需要将循环引用的两个类其中一个智能指针由强指针声明为弱指针即可。具体如何实现上代码。
标头.h
#pragma once
class CRefCount
{
public:
CRefCount() {
m_nUsedCount = 1;
m_nWeakCount = 1;
}
void incUsed() {
m_nUsedCount++;
}
int decUsed() {
m_nUsedCount--;
return m_nUsedCount;
}
void incWeak() {
m_nWeakCount++;
}
int decWeak() {
m_nWeakCount--;
return m_nWeakCount;
}
int getUsed() {
return m_nUsedCount;
}
private:
int m_nUsedCount; //强指针引用次数
int m_nWeakCount; //弱指针引用次数
};
template
class CMySmartPtrbase//智能指针的基类,作提高接口使用
{
public:
CMySmartPtrbase() {};
~CMySmartPtrbase() {};
void destroy() {
delete m_Ptr;
}
void release() {
if (m_pRef != nullptr && m_pRef->decWeak() == 0) {
delete m_pRef;
}
}
protected:
T* m_Ptr;
CRefCount* m_pRef;
};
//强指针类型
template
class CMyWeakPtr;
template
class CStrongPtr : public CMySmartPtrbase
{
friend class CMyWeakPtr;
public:
CStrongPtr() {
this->m_Ptr = nullptr;
this->m_pRef = nullptr;
}
explicit CStrongPtr(T* p) {
this->m_Ptr = p;
this->m_pRef = new CRefCount;
}
CStrongPtr(CStrongPtr& obj) {
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incUsed();
this->m_pRef = obj.m_pRef;
}
CStrongPtr& operator=(CStrongPtr& obj) {
if (this->m_pRef != nullptr && this->m_pRef->decUsed() == 0) {
this->destroy();
this->release();
}
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incUsed();
this->m_pRef = obj.m_pRef;
return *this;
}
CStrongPtr(CMyWeakPtr& obj) {
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incUsed();
this->m_pRef = obj.m_pRef;
}
~CStrongPtr() {
if (this->m_pRef != nullptr && this->m_pRef->decUsed() == 0) {
this->destroy();
this->release();
}
}
T& operator*() {
return *(this->m_Ptr);
}
T* operator->() {
return this->m_Ptr;
}
T* get() {
return this->m_Ptr;
}
};
//弱指针类型
template
class CMyWeakPtr : public CMySmartPtrbase
{
public:
friend class CStrongPtr;
CMyWeakPtr() {
this->m_Ptr = nullptr;
this->m_pRef = nullptr;
}
CMyWeakPtr(CStrongPtr& obj) {
//release();
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incWeak();
this->m_pRef = obj.m_pRef;
}
CMyWeakPtr& operator = (CStrongPtr& obj) {
this->release();
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incWeak();
this->m_pRef = obj.m_pRef;
return *this;
}
CMyWeakPtr(CMyWeakPtr& obj) {
this->m_Ptr = obj.m_Ptr;
obj.m_pRef->incWeak();
this->m_pRef = obj.m_pRef;
}
~CMyWeakPtr() {
this->release();
}
CStrongPtr& lock() {
if (this->m_pRef == nullptr) {
return CStrongPtr();
}
return CStrongPtr(*this);
}
bool IsExpried() {
if (this->m_pRef == nullptr) {
return true;
}
return this->m_pRef->getUsed() == 0;
}
};
源.cpp
#include#include"标头.h" class CSon; class CTest { public: void set(CStrongPtr p2) { m_p1 = p2; } CStrongPtr m_p1; }; class CSon { public: void set(CStrongPtr p2) { m_p1 = p2; } CMyWeakPtr m_p1; }; void foo() { CTest* father = new CTest(); CSon* son = new CSon(); CStrongPtr ptrFather(father); CStrongPtr ptrSon(son); father->set(ptrSon); son->set(ptrFather); } int main() { foo(); return 0; }
单步跳过结果展示
未释放前
析构释放后
总结:跟踪智能指针源码可知



