C++ 智能指针完全指南:原理、用法与避坑实战(从 RAII 到循环引用)


🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:


文章目录

  • 前言:
  • [一. 智能指针的核心:RAII 设计思想](#一. 智能指针的核心:RAII 设计思想)
    • [1.1 为什么需要智能指针?](#1.1 为什么需要智能指针?)
    • [1.2 RAII:智能指针的设计灵魂](#1.2 RAII:智能指针的设计灵魂)
  • [二. C++ 标准库智能指针:用法与场景](#二. C++ 标准库智能指针:用法与场景)
    • [2.1 unique_ptr:独占式智能指针(推荐优先使用)](#2.1 unique_ptr:独占式智能指针(推荐优先使用))
    • [2.2 shared_ptr:共享式智能指针(支持拷贝,重点了解)](#2.2 shared_ptr:共享式智能指针(支持拷贝,重点了解))
    • [2.3 weak_ptr:弱引用智能指针(解决循环引用)](#2.3 weak_ptr:弱引用智能指针(解决循环引用))
      • [2.3.1 循环引用问题(`shared_ptr`的致命缺陷)](#2.3.1 循环引用问题(shared_ptr的致命缺陷))
      • [2.3.2 用weak_ptr解决循环引用](#2.3.2 用weak_ptr解决循环引用)
      • [2.3.3 weak_ptr访问资源](#2.3.3 weak_ptr访问资源)
    • [2.4 使用示例演示](#2.4 使用示例演示)
  • [三. shared_ptr的线程安全问题](#三. shared_ptr的线程安全问题)
  • [四. C++11和boost中智能指针的关系](#四. C++11和boost中智能指针的关系)
  • [五. 内存泄漏:大型项目设计必备](#五. 内存泄漏:大型项目设计必备)
    • [5.1 什么是内存泄漏,内存泄漏的危害](#5.1 什么是内存泄漏,内存泄漏的危害)
    • [5.2 如果检查内存泄露(了解)](#5.2 如果检查内存泄露(了解))
    • [5.3 如何避免内存泄漏](#5.3 如何避免内存泄漏)
  • 结尾:

前言:

在 C++ 开发中,内存泄漏是长期困扰开发者的核心痛点 ------ 手动管理new/delete时,一旦遇到异常、跳转或疏忽,就可能导致资源无法释放。而智能指针的出现,通过 RAII(资源获取即初始化)机制完美解决了这一问题:它将资源托管给对象,利用对象生命周期自动释放资源,让开发者无需关注手动释放,专注业务逻辑。本文结合从智能指针的设计思想(RAII)入手,详解 C++ 标准库中unique_ptrshared_ptrweak_ptr的用法、原理、适用场景,再到删除器定制、循环引用解决、线程安全等实战问题,帮你彻底掌握智能指针,写出安全、高效的 C++ 代码。


一. 智能指针的核心:RAII 设计思想

1.1 为什么需要智能指针?

手动管理内存的致命问题:异常导致资源泄漏。例如下面的代码,Divide抛出异常后,array1array2delete语句无法执行,造成内存泄漏:

cpp 复制代码
double Divide(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}
void Func()
{
	// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array和array2没有得到释放。
	// 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。
	// 但是如果array2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案
	// 是智能指针,否则代码太戳了
	int* array1 = new int[10];
	int* array2 = new int[10]; // 抛异常呢
	try
	{
		int len, time;
		cin >> len >> time;
		cout << Divide(len, time) << endl;
	}
	catch (...)
	{
		cout << "delete []" << array1 << endl;
		cout << "delete []" << array2 << endl;
		delete[] array1;
		delete[] array2;
		throw; // 异常重新抛出,捕获到什么抛出什么
	}
	// ...
	cout << "delete []" << array1 << endl;
	delete[] array1;
	cout << "delete []" << array2 << endl;
	delete[] array2;
}
int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}
	return 0;
}

1.2 RAII:智能指针的设计灵魂

  • RAII(Resource Acquisition Is Initialization)即 "资源获取即初始化",他是一种管理资源的类的设计思想,本质是一种利用对象生命周期来管理获取到的动态资源,避免资源泄露,这里的资源可以是内存,文件指针,网络连接,互斥锁等等。RAII 在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源,这样保障了资源的正常释放,避免资源泄露问题。
  • 智能指针类除了满足RAII的设计思路,还要方便资源的访问,所以智能指针类还会像迭代器类一样,重载 operator*/operator->/operator[] 等运算符,方便访问资源。

RAII 核心思想:

  • 资源(内存、文件句柄、锁)在对象构造时获取;
  • 资源在对象析构时自动释放(无论正常执行还是异常退出,对象生命周期结束都会调用析构);
  • 智能指针本质是封装了指针的类,重载*->等运算符,模拟指针行为,同时通过 RAII 管理资源。

简易智能指针实现(理解原理)

cpp 复制代码
template<class T>
class SmartPtr
{
public:
	// 构造时获取资源(RAII)
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}
	
	// 析构时自动释放资源
	~SmartPtr()
	{
		cout << "delete []:" << _ptr << endl;
		delete[] _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator[](size_t i)
	{
		return _ptr[i];
	}
private:
	T* _ptr;
};

//int main()
//{
//	SmartPtr<int> sp1 = new int[5] {1, 2, 3, 4, 5};
//	*sp1 += 1;
//	sp1[2] += 1;
//
//	SmartPtr<pair<int, int>> sp2 = new pair<int, int>[2];
//	sp2->first = 1;
//	sp2->second = 1;
//
//	return 0;
//}

使用后,即使抛出异常,SmartPtr对象析构时也会自动释放资源,代码简洁且安全:

cpp 复制代码
double Divide(int a, int b)
{
	// 当 b == 0 时抛异常
	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}

void Func()
{
	SmartPtr<int> sp1 = new int[10];
	SmartPtr<int> sp2 = new int[10];

	int len, time;
	cin >> len >> time;
	cout << Divide(len, time) << endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

二. C++ 标准库智能指针:用法与场景

C++11 及后续标准提供了 3 种核心智能指针(均在<memory>头文件中),各自针对不同场景设计,先简单介绍一下,后面逐一详解。

  • auto_ptr 是C++98时设计出来的智能指针,他的特点是拷贝时把拷贝对象的资源的管理权转移给拷贝对象,这是一个非常糟糕的设计,因为他会导致被拷贝对象悬空,访问报错的问题,C++11设计出新的智能指针后,强烈建议不要使用auto_ptr。其实C++11出来之前很多公司也是明令禁止使用这个智能指针的。
  • unique_ptr 是C++11设计出来的智能指针,他的名字翻译出来是唯一指针,他的特点的不支持拷贝,只支持移动。如果不需要拷贝的场景就非常建议使用他。
  • shared_ptr 是C++11设计出来的智能指针,他的名字翻译出来是共享指针,他的特点是支持拷贝,也支持移动。如果需要拷贝的场景就需要使用他了。底层是用引用计数的方式实现的。
  • weak_ptr 是C++11设计出来的智能指针,他的名字翻译出来是弱指针,他完全不同于上面的智能指针,他不支持RAII,也就意味着不能用它直接管理资源,weak_ptr 的产生本质是要解决 shared_ptr 的一个循环引用导致内存泄露的问题。

补充:

智能指针 核心特性 适用场景 效率 注意事项
unique_ptr 独占资源,不支持拷贝 局部变量、函数返回值、容器元素 最高 移动后原对象悬空
shared_ptr 共享资源,支持拷贝 多对象共享资源、多线程共享数据 中等 避免循环引用(用 weak_ptr 解决)
weak_ptr 弱引用,不管理资源 解决 shared_ptr 循环引用、观察资源 需 lock() 获取 shared_ptr 才能访问资源
cpp 复制代码
#include<memory>
class A
{
public:
	A(int a1 = 1, int a2 = 1)
		:_a1(a1)
		, _a2(a2)
	{
		cout << "A()" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}

	int _a1 = 1;
	int _a2 = 1;
};


int main()
{
	// 拷贝对象存在问题
	SmartPtr<int> sp1 = new int[10];
	SmartPtr<int> sp2(sp1);

	auto_ptr<A> ap1(new A);
	ap1->_a1++;

	// 管理全转移,ap1悬空
	auto_ptr<A> ap2(ap1);
	// ap1->_a1++; // 这个时候就报错了
	ap2->_a1++;

	unique_ptr<A> up1(new A);
	// 不允许拷贝
	// unique_ptr<A> up2(up1);
	// unique_ptr<A> up2(move(up1)); // 可以移动
	up1->_a1++;

	up1.release();

	// if(up1)
	if (up1.operator bool())
	{
		cout << "up1不为空" << endl;
	}
	else
	{
		cout << "up1为空" << endl;
	}

	shared_ptr<A> sp3(new A);
	// 支持拷贝
	shared_ptr<A> sp4(sp3);
	sp3->_a1++;

	return 0;

}

2.1 unique_ptr:独占式智能指针(推荐优先使用)

unique_ptr意为 "唯一指针",核心特性:资源独占,不支持拷贝,仅支持移动,效率最高 (无引用计数开销)。

核心用法

cpp 复制代码
#include <memory>
struct Date {
    int _year, _month, _day;
    Date(int y=1, int m=1, int d=1) : _year(y), _month(m), _day(d) {}
    ~Date() { cout << "~Date()" << endl; }
};

int main() {
    // 构造:托管资源
    unique_ptr<Date> up1(new Date(2024, 10, 1));
    
    // 支持指针操作
    up1->_year = 2025;
    cout << up1->_year << endl;

    // 不支持拷贝(编译报错)
    // unique_ptr<Date> up2(up1);

    // 支持移动(资源所有权转移,up1悬空)
    unique_ptr<Date> up3(move(up1));
    if (!up1) cout << "up1已悬空" << endl;

    // 管理数组(特化版本,析构时用delete[])
    unique_ptr<Date[]> up4(new Date[5]);

    return 0;
}

适用场景:

  • 局部变量、函数返回值(无需共享资源);
  • 容器元素(避免拷贝开销);
  • 替代auto_ptrauto_ptr拷贝时转移所有权,易导致悬空指针,已被废弃)。

auto_ptr && unique_ptr 模拟实现(了解即可)

cpp 复制代码
namespace Lotso
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{
		}
		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			// 管理权转移
			sp._ptr = nullptr;
		}
		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{// 检测是否为⾃⼰给⾃⼰赋值
			if (this != &ap)
			{
				// 释放当前对象中资源
				if (_ptr)
					delete _ptr;
				// 转移ap中资源到当前对象中
				_ptr = ap._ptr;
				ap._ptr = NULL;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		// 像指针⼀样使⽤
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};


	template<class T>
	class unique_ptr
	{
	public:
		explicit unique_ptr(T* ptr)
			:_ptr(ptr)
		{
		}
		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		// 像指针⼀样使⽤
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		unique_ptr(const unique_ptr<T>&sp) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>&sp) = delete;
		unique_ptr(unique_ptr<T> && sp)
			:_ptr(sp._ptr)
		{
			sp._ptr = nullptr;
		}
		unique_ptr<T>& operator=(unique_ptr<T> && sp)
		{
			delete _ptr;
			_ptr = sp._ptr;
			sp._ptr = nullptr;
		}
	private:
		T* _ptr;
	};
}

2.2 shared_ptr:共享式智能指针(支持拷贝,重点了解)

shared_ptr意为 "共享指针",核心特性:支持拷贝和移动,通过引用计数管理资源,多个shared_ptr可托管同一资源,引用计数为 0 时自动释放。
核心原理:

  • 引用计数:堆上维护一个计数器,记录当前托管该资源的shared_ptr数量;
  • 构造 / 拷贝:计数器 + 1;
  • 析构 :计数器 - 1,计数器为 0 时释放资源。当然赋值后面是会 计数器+ 的

核心用法

cpp 复制代码
int main() {
    // 构造:托管资源,计数器初始为1
    shared_ptr<Date> sp1(new Date(2024, 10, 1));
    cout << "sp1引用计数:" << sp1.use_count() << endl; // 输出1

    // 拷贝:计数器+1
    shared_ptr<Date> sp2(sp1);
    shared_ptr<Date> sp3 = sp2;
    cout << "sp1引用计数:" << sp1.use_count() << endl; // 输出3

    // 移动:所有权转移,原对象悬空,计数器不变
    shared_ptr<Date> sp4(move(sp1));
    cout << "sp1是否为空:" << (sp1 ? false : true) << endl; // 输出true
    cout << "sp4引用计数:" << sp4.use_count() << endl; // 输出3

    // 管理数组(特化版本)
    shared_ptr<Date[]> sp5(new Date[5]);

    // 推荐:用make_shared构造(更高效,避免内存泄漏风险)
    auto sp6 = make_shared<Date>(2024, 10, 2);

    return 0;
}

适用场景:

  • 资源需要被多个对象共享(如容器中存储的对象、多线程共享数据);
  • 无法确定哪个对象最后释放资源的场景。



shared_ptr 模拟实现:(重点,附带简易版 weak_ptr)

cpp 复制代码
#include<functional>
#include<atomic>

namespace Lotso
{
	template<class T>
	class shared_ptr
	{
	public:
		explicit shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pcount(new int(1))
			// ,_pcount(new atomic<int>(1))
		{
		}

		// RAII
		template<class D>
		explicit shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _del(del)
		{
		}

		~shared_ptr()
		{
			// 引用计数减到 0 ,说明最后一个管理智能指针对象,要释放资源
			release();
		}

		shared_ptr(const shared_ptr& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
		{
			// 加加计数
			++(*_pcount);
		}

		void release()
		{
			if (--(*_pcount) == 0)
			{
				// cout << "delete []:" << _ptr << endl;
				// delete _ptr;
				_del(_ptr);

				delete _pcount;
			}
		}
		// sp1 = sp3
		shared_ptr& operator=(const shared_ptr& sp)
		{
			// if (this != &sp)
			if (_ptr != sp._ptr)
			{
				release();

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				++(*_pcount);
			}

			return *this;
		}

		T* get() const
		{
			return _ptr;
		}

		int use_count()
		{
			return *_pcount;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		T& operator[](size_t i)
		{
			return _ptr[i];
		}


	private:
		T* _ptr;
		int* _pcount;
		// atomic<int>* _pcount;

		function<void(T*)> _del = [](T* ptr) {delete ptr; };
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
		{
		}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{
		}

		// 不参与资源的管理

	private:
		T* _ptr = nullptr;
		// int* _pcount;
	};
}

//int main()
//{
//	Lotso::shared_ptr<A> sp1(new A);
//	Lotso::shared_ptr<A> sp2(sp1);
//	sp1 = sp1;
//	sp1 = sp2;
//
//	Lotso::shared_ptr<A> sp3(new A);
//
//	sp1 = sp3;
//}

2.3 weak_ptr:弱引用智能指针(解决循环引用)

weak_ptr是为解决shared_ptr的循环引用问题而生,核心特性:

  • 不支持 RAII,不能直接托管资源;
  • 仅能从shared_ptr构造,绑定后不增加引用计数;
  • 不重载*->,需通过lock()获取shared_ptr才能访问资源;
  • 支持expired()判断资源是否已释放。

2.3.1 循环引用问题(shared_ptr的致命缺陷)

当两个shared_ptr互相引用时,引用计数无法减到 0,导致资源泄漏:

分析

cpp 复制代码
struct ListNode {
    int _data;
    shared_ptr<ListNode> _next; // 互相引用
    shared_ptr<ListNode> _prev;

    ~ListNode() { cout << "~ListNode()" << endl; }
};

int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);
    cout << n1.use_count() << "," << n2.use_count() << endl; // 输出1,1

    n1->_next = n2; // n2计数+1 → 2
    n2->_prev = n1; // n1计数+1 → 2

    // 析构n1和n2:计数各减1 → 1,无法释放资源(循环引用)
    cout << n1.use_count() << endl;
    cout << n2.use_count() << endl;
	
    return 0;
}

2.3.2 用weak_ptr解决循环引用

将互相引用的成员改为weak_ptr,不参与引用计数管理,打破循环:

cpp 复制代码
struct ListNode {
    int _data;
    weak_ptr<ListNode> _next; // 弱引用,不增加计数
    weak_ptr<ListNode> _prev;

    ~ListNode() { cout << "~ListNode()" << endl; }
};

int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);

    n1->_next = n2; // weak_ptr绑定shared_ptr,n2计数仍为1
    n2->_prev = n1; // weak_ptr绑定shared_ptr,n1计数仍为1

	// 析构n1和n2:计数各减1 → 0,资源释放(输出~ListNode()两次)
	cout << n1.use_count() << endl;
	cout << n2.use_count() << endl;

    return 0;
}

2.3.3 weak_ptr访问资源

cpp 复制代码
int main()
{
	std::shared_ptr<string> sp1(new string("111111"));
	std::shared_ptr<string> sp2(sp1);

	std::weak_ptr<string> wp = sp1;
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl << endl;

	// sp1和sp2都指向了其他资源,则weak_ptr就过期了
	sp1 = make_shared<string>("222222");
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl << endl;
	
	// 没有过期,通过lock拷贝一个shared_ptr对象来访问资源
	if(!wp.expired())
	{
		auto sp = wp.lock();
		cout << wp.expired() << endl;
		cout << wp.use_count() << endl << endl;
		*sp += "xxxxxxxx";
	}

	sp2 = make_shared<string>("333333");
	cout << wp.expired() << endl;
	cout << wp.use_count() << endl << endl;

	return 0;
}

2.4 使用示例演示

定制删除器(管理非 new 资源)

智能指针默认用delete释放资源,若托管的是new[]、文件句柄、锁等资源,需定制删除器(本质是可调用对象:仿函数、函数指针、lambda)。

cpp 复制代码
template<class T>
void DeleteArrayFunc(T* ptr)
{
	delete[] ptr;
}

template<class T>
class DeleteArray
{
public:
	void operator()(T* ptr)
	{
		delete[] ptr;
	}
};

int main()
{
	// 定制删除器
	Lotso::shared_ptr<A> sp1(new A[10], DeleteArray<A>());// 仿函数
	Lotso::shared_ptr<A> sp2(new A[10], DeleteArrayFunc<A>);// 函数指针

	// 推荐
	Lotso::shared_ptr<A> sp3(new A[10], [](A* ptr) {delete[] ptr; });// lambda
	Lotso::shared_ptr<FILE> sp4(fopen("Test.cpp", "r"), [](FILE* ptr) {fclose(ptr); });
	Lotso::shared_ptr<A> sp5(new A);

	// 删除器的位置是一致的,shared_ptr在构造函数参数,unique_ptr类模版的参数
	// 这里没有使用相同的方式还是挺坑的
	// 使用仿函数unique_ptr可以不在构造函数传递,因为仿函数类型构造的对象直接就可以调用
	// 但是下面的函数指针和lambda的类型不可以
	std::unique_ptr<A, DeleteArray<A>> up1(new A[10]); // 仿函数
	std::unique_ptr<A, void(*)(A*)> up2(new A[10], DeleteArrayFunc<A>);// 函数指针
	auto del = [](A* ptr) {delete[] ptr; };
	std::unique_ptr<A, decltype(del)> up3(new A[10], del); // lambda

	// 更简洁的方式
	// 因为new[]经常使用,所以unique_ptr和shared_ptr
	// 实现了⼀个特化版本,这个特化版本析构时用的delete[]
	std::shared_ptr<A[]> sp10(new A[10]);
	std::unique_ptr<A[]> up10(new A[10]);

	auto sp11 = make_shared<A>(1, 1);

	return 0;
}

三. shared_ptr的线程安全问题

  • shared_ptr的引用计数对象在堆上,如果多个shared_ptr对象在多个线程中,进行shared_ptr的拷贝析构时会访问修改引用计数,就会存在线程安全问题。所以shared_ptr引用计数是需要加锁或者原子操作保证线程安全的。
  • shared_ptr指向的对象也是有线程安全问题的,但是这个对象的线程安全问题不归shared_ptr管,它也管不了,应该有外层使用shared_ptr的人进行线程安全的控制。
  • 下面的程序会崩溃或者A资源没释放, Lotso::shared_ptr引用计数从 int* 改成 atomic<int>* 就可以保证引用计数的线程安全问题,或者使用互斥锁加锁也可以。
cpp 复制代码
struct AA
{
	int _a1 = 0;
	int _a2 = 0;
	~AA()
	{
		cout << "~AA()" << endl;
	}
};
int main()
{
	Lotso::shared_ptr<AA> p(new AA);
	const size_t n = 100000;
	mutex mtx;
	auto func = [&]()
		{
			for (size_t i = 0; i < n; ++i)
			{
				// 这⾥智能指针拷⻉会++计数
				Lotso::shared_ptr<AA> copy(p);
				{
					unique_lock<mutex> lk(mtx);
					copy->_a1++;
					copy->_a2++;
				}
			}
		};
	thread t1(func);
	thread t2(func);
	t1.join();
	t2.join();
	cout << p->_a1 << endl;
	cout << p->_a2 << endl;
	cout << p.use_count() << endl;
	return 0;
}

四. C++11和boost中智能指针的关系

  • Boost库是为C++标准库提供扩展的一些C++程序库的总称,Boost社区建议的初衷之一就是为C++的标准化工作提供可参考的实现,Boost社区的发起人Dawes本人就是C++标准委员会的成员之⼀。在Boost库的开发中,Boost社区也在这个方向上取得了丰硕的成果,C++11及之后的新语法和库有很多都是从Boost中来的。
  • C++ 98 中产生了第⼀个智能指针auto_ptr
  • C++ boost给出了更实用的scoped_ptr/scoped_arrayshared_ptr/shared_arrayweak_ptr等.
  • C++ TR1,引入了shared_ptr等,不过注意的是TR1并不是标准版。
  • C++ 11,引⼊了unique_ptrshared_ptrweak_ptr。需要注意的是unique_ptr对应boost的
    scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。

五. 内存泄漏:大型项目设计必备

5.1 什么是内存泄漏,内存泄漏的危害

  • 什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存,一般是忘记释放或者发生异常释放程序未能执行导致的。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对改段内存的控制,因而造成了内存的浪费。
  • 内存泄漏的危害 :普通程序运行一会就结束了出现内存泄漏问题也不大,进程正常结束,页表的映射
    关系解除,物理内存也可以释放。长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务、长时间运行的客户端等等,不断出现内存泄漏会导致可用内存不断变少,各种功能响应越来越慢,最终卡死。
cpp 复制代码
int main()
{
	// 申请一个1G未释放,这个程序多次运行也没啥危害
	// 因为程序马上就结束,进程结束各种资源也就回收了
	char* ptr = new char[1024 * 1024 * 1024];
	cout << (void*)ptr << endl;

	return 0;
}

5.2 如果检查内存泄露(了解)

5.3 如何避免内存泄漏

  • 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。ps: 这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一个智能指针来管理才有保证。
  • 尽量使用智能指针来管理资源,如果自己面临的场景比较特殊,采用RAII思想自己造个轮子管理。
  • 定期使用内存泄露工具检测,尤其是项目上线之前,不过有些工具不台靠谱,或者是要收费。
  • 总结一下:内存泄露非常常见,解决方案无非就是两种:1. 事前预防型。如智能指针等。2. 事后查错型。如泄漏检测工具。

结尾:

html 复制代码
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:智能指针是 C++ 中解决内存泄漏的 "终极武器",掌握其 RAII 设计思想、三种核心指针的用法与场景,能让你的代码更安全、更易维护。实际开发中,优先使用unique_ptr(高效无开销),需要共享资源时用shared_ptr,遇到循环引用时用weak_ptr配合,再结合定制删除器处理特殊资源,即可覆盖绝大多数场景。建议在项目中彻底替代手动new/delete,用智能指针托管所有动态资源,从根源上杜绝内存泄漏。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
超龄编码人几秒前
Qt Widgets Designer QTabWidget无法添加布局
开发语言·qt
xixixi77777几秒前
英伟达Agent专用全模态模型出击,仿冒AI智能体泛滥成灾,《AI伦理安全指引》即将落地——AI治理迎来“技术-风险-规范”三重奏
人工智能·5g·安全·ai·大模型·英伟达·智能体
直奔標竿3 分钟前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
数据皮皮侠AI7 分钟前
中国城市可再生能源数据集(2005-2021)|顶刊 Sci Data 11 种能源面板
大数据·人工智能·笔记·能源·1024程序员节
Python大数据分析@9 分钟前
CLI一键采集,使用Python搭建TikTok电商爬虫Agent
开发语言·爬虫·python
G311354227311 分钟前
如何用 QClaw 龙虾做一个规律作息健康助理 Agent
大数据·人工智能·ai·云计算
幂律智能12 分钟前
零售行业合同管理数智化转型解决方案
大数据·人工智能·零售
旺财矿工13 分钟前
零基础搭建 OpenClaw 2.6.6 Win11 本地化运行环境
人工智能·openclaw·小龙虾·龙虾·openclaw安装包
九成宫14 分钟前
动手学深度学习PyTorch版初步安装过程
人工智能·pytorch·深度学习
Traving Yu15 分钟前
Prompt提示词工程
人工智能·prompt