C++ 智能指针:高效的内存管理利器以及sharp_ptr模拟实现

目录

[一、RAII 原理](#一、RAII 原理)

二、智能指针的出现背景

三、不同类型的智能指针

[1. std::auto_ptr](#1. std::auto_ptr)

[2. std::unique_ptr](#2. std::unique_ptr)

[3. std::shared_ptr](#3. std::shared_ptr)

[4. std::weak_ptr](#4. std::weak_ptr)

四、总结

五、模拟实现shared_ptr:

shared_ptr.h

test.cpp

在 C++ 编程中,内存管理一直是一个重要且具有挑战性的问题。手动管理内存容易导致内存泄漏、悬空指针等问题,而 C++ 的智能指针则为我们提供了一种更安全、更高效的内存管理方式。同时,智能指针的实现也与 RAII(Resource Acquisition Is Initialization,资源获取即初始化)原理紧密相关。

一、RAII 原理

RAII 的核心思想是在构造函数中获取资源(如动态分配的内存、文件句柄等),并在析构函数中释放资源。这样可以确保资源在对象的生命周期内始终得到正确的管理,无论对象是通过正常路径还是异常路径退出作用域。

例如,下面的代码展示了一个使用 RAII 管理文件资源的类:

cpp 复制代码
class FileHandler {
public:
    FileHandler(const std://string& filename) : file_(std::fopen(filename.c_str(), "r")) {}
    ~FileHandler() {
        if (file_) {
            std::fclose(file_);
        }
    }
    // 其他成员函数
private:
    FILE* file_;
};

在这个例子中,FileHandler类的构造函数打开文件,析构函数关闭文件。无论在使用文件的过程中是否发生异常,文件都会被正确地关闭。

二、智能指针的出现背景

Java 有垃圾回收机制(GC),不需要开发者手动释放内存,这大大简化了内存管理。然而,C++ 为了追求更高的效率,选择让开发者自己管理内存。但当程序中存在异常时,手动管理内存就变得有些困难,因为可能会导致资源在异常发生时没有被正确释放。为了解决这个问题,C++ 引入了智能指针。

三、不同类型的智能指针

1. std::auto_ptr

std::auto_ptr是 C++98 引入的一种智能指针,它通过转移所有权的方式实现资源管理。当一个std::auto_ptr对象被拷贝时,所有权会转移到新的对象,导致原对象悬空。

cpp 复制代码
std::auto_ptr<int> ptr1(new int(10));
std::auto_ptr<int> ptr2 = ptr1; // ptr1 现在悬空,ptr2 拥有资源

由于std::auto_ptr存在一些问题,在 C++11 中已被弃用。

2. std::unique_ptr

std::unique_ptr是 C++11 引入的智能指针,它禁止拷贝,通过删除拷贝构造函数和赋值运算符来实现。这确保了资源的唯一所有权,避免了资源的重复释放。

cpp 复制代码
std::unique_ptr<int> ptr1(new int(10));
// std::unique_ptr<int> ptr2 = ptr1; // 编译错误,禁止拷贝

std::unique_ptr还支持定制删除器,可以在模板参数中传递一个函数对象,用于在资源释放时执行特定的操作

cpp 复制代码
void customDeleter(int* p) {
    std::cout << "自定义删除器被调用" << std::endl;
    delete p;
}

std::unique_ptr<int, decltype(customDeleter)> ptr(new int(10), customDeleter);

3. std::shared_ptr

std::shared_ptr通过引用计数实现资源的共享所有权。多个std::shared_ptr对象可以指向同一块内存,当最后一个指向该内存的std::shared_ptr对象被销毁时,资源才会被释放。

cpp 复制代码
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // 引用计数增加

std::shared_ptr可以在函数参数中传递,方便地实现资源的共享。

cpp 复制代码
void func(std::shared_ptr<int> ptr) {
    // 使用资源
}

int main() {
    std::shared_ptr<int> ptr(new int(10));
    func(ptr);
    return 0;
}

4. std::weak_ptr

std::weak_ptr是一种弱引用智能指针,它不直接管理资源,也不增加资源的引用计数。它通常与std::shared_ptr配合使用,用于解决循环引用导致的内存泄漏问题。

cpp 复制代码
class A;
class B;

class A {
public:
    std::shared_ptr<B> ptrB;
    ~A() {
        std::cout << "A 被销毁" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> ptrA;
    ~B() {
        std::cout << "B 被销毁" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a(new A());
    std::shared_ptr<B> b(new B());
    a->ptrB = b;
    b->ptrA = a;
    return 0;
}

在上面的代码中,AB类相互持有对方的std::shared_ptr,导致循环引用,即使离开main函数的作用域,资源也不会被释放。使用std::weak_ptr可以解决这个问题:

cpp 复制代码
class A;
class B;

class A {
public:
    std::shared_ptr<B> ptrB;
    ~A() {
        std::cout << "A 被销毁" << std::endl;
    }
};

class B {
public:
    std::weak_ptr<A> ptrA;
    ~B() {
        std::cout << "B 被销毁" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a(new A());
    std::shared_ptr<B> b(new B());
    a->ptrB = b;
    b->ptrA = a;
    return 0;
}

现在,当离开main函数的作用域时,资源会被正确释放。

四、总结

C++ 的智能指针是一种强大的工具,可以帮助我们更有效地管理内存资源。通过 RAII 原理,智能指针在构造函数中获取资源,在析构函数中释放资源,确保资源的正确管理。不同类型的智能指针(std::unique_ptrstd::shared_ptrstd::weak_ptr)提供了不同的功能,可以根据具体的需求选择合适的智能指针。在使用智能指针时,我们需要注意避免一些常见的陷阱,如循环引用等问题,以确保程序的正确性和稳定性。

五、模拟实现shared_ptr:

shared_ptr.h

cpp 复制代码
#pragma once
#include<iostream>
#include<functional>
using namespace std;

namespace xxx {

	

	template<class T>
	struct ListNode
	{
	public:
		ListNode(const T& data = T()) : _data(data), _next(nullptr) {}
		ListNode* _next = nullptr;
		T _data = T();
	};

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr, function<void(T*)> func = [](T* ptr)
			{
				delete ptr;
			})
			:_ptr(ptr),
				_func(func)
		{
			*_pcount = 0;
			(*_pcount)++;
		}
			shared_ptr(shared_ptr<T>& other)
			{
				_ptr = other._ptr;
				_func = other._func;
				_pcount = other._pcount;
				(*_pcount)++;
			}
			shared_ptr<T>* operator=(shared_ptr<T>& other)
			{
				if (other._ptr == _ptr)
					return this;
				del();
				_ptr = other._ptr;
				_func = other._func;
				_pcount = other._pcount;
				(*_pcount)++;
				return this;
			}
			~shared_ptr()
			{
				del();
			}
			void del()
			{
				if (--(*_pcount) == 0)
				{
					_func(_ptr);
					delete _pcount;
					_pcount = nullptr;
					_ptr = nullptr;
				}
				cout << "void del()" << endl;
			}
			T* operator->()
			{
				return _ptr;
			}
			T& operator*()
			{
				return *_ptr;
			}

			T* _ptr;
			function<void(T*)> _func;
			int* _pcount = new int;
	};


}

test.cpp

cpp 复制代码
#include"share_ptr.h"
class Date
{
public:
	Date(int year = int(), int month = int(), int day = int())
		:_year(year),
		_month(month),
		_day(day)
	{

	}

	~Date()
	{
		cout << "~Data()" << endl;
	}
	int _year;
	int _month;
	int _day;
};
int main()
{
	xxx::shared_ptr<Date> sp1(new Date);
	xxx::shared_ptr<Date> sp2(sp1);
	xxx::shared_ptr<Date> sp3(new Date);

	// 自己给自己赋值
	sp3 = sp3;
	sp1 = sp2;

	sp1 = sp3;
	sp2 = sp3;

	xxx::shared_ptr<int> sp6((int*)malloc(40), [](int* ptr) 
		{
			cout << "free:" << ptr << endl;
			free(ptr);
		});
	return 0;
}
相关推荐
梦想科研社10 分钟前
【无人机设计与控制】红嘴蓝鹊优化器RBMO求解无人机路径规划MATLAB
开发语言·matlab·无人机
混迹网络的权某12 分钟前
每天一道C语言精选编程题之求数字的每⼀位之和
c语言·开发语言·考研·算法·改行学it·1024程序员节
一只特立独行的猪6111 小时前
Java面试题——微服务篇
java·开发语言·微服务
Ddddddd_1583 小时前
C++ | Leetcode C++题解之第504题七进制数
c++·leetcode·题解
J_z_Yang3 小时前
LeetCode 202 - 快乐数
c++·算法·leetcode
喵手3 小时前
Java 与 Oracle 数据泵实操:数据导入导出的全方位指南
java·开发语言·oracle
硬汉嵌入式4 小时前
H7-TOOL的LUA小程序教程第16期:脉冲测量,4路PWM,多路GPIO和波形打印(2024-10-25, 更新完毕)
开发语言·junit·小程序·lua
Wx120不知道取啥名4 小时前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
Python私教5 小时前
Flutter颜色和主题
开发语言·javascript·flutter
代码吐槽菌5 小时前
基于SSM的汽车客运站管理系统【附源码】
java·开发语言·数据库·spring boot·后端·汽车