目录

一、定义

二、自定义智能指针

1.自定义成员工具函数:void reset()

2.*/->关键区分

3.进阶:将自定义的智能指针提升为类模板

三、四种类型的智能指针

先说一下auto_ptr的缺陷

1.unique_ptr

1.1 语法

1.2 常用成员函数

1.3 核心特点

1.4 适用场景

不适合

2.shared_ptr

2.1 定义语法

2.2 常用成员函数

2.3 核心特性

2.4 适用场景

不适合

3.weak_ptr

3.1 定义语法

3.2 常用成员函数

3.3 核心特性

3.4 适用场景

不适合


一、定义

C++11智能指针是封装了普通指针的类模板,核心作用是自动管理内存——利用RALL(资源获取即初始化)机制,在智能指针对象生命周期结束时(出作用域、析构),自动释放指向的堆内存,从根本上避免内存泄漏和野指针问题。

二、自定义智能指针

为了更好的理解智能指针的原理,我们来是手写一个智能指针

//定义结构体person
struct Person {
	int pid;
	string name;
	int age;
public:
	Person(int pid,int age,string name):pid(pid),name(name),age(age){}

	void show() {
		cout << pid << " | " << name << " | " << age << endl;
	}

	~Person() {
		cout << "~Person():" << this << endl;
	}
};

//智能指针类

class AutoPersonPtr {
	Person* p;//指向堆区new person地址
public:
	AutoPersonPtr(Person*p):p(p){}//1.接收外部已经new好的指针

	AutoPersonPtr(int pid, int age, string name) {//2.传入参数,构造函数内部帮你new
		p = new Person(pid, age, name);
	}

	AutoPersonPtr(const AutoPersonPtr& o) = delete;//禁用拷贝构造
	void operator=(const AutoPersonPtr& o) = delete;//禁用拷贝赋值重载
	AutoPersonPtr(AutoPersonPtr&& o) = delete;//禁用移动构造
	void operator=(AutoPersonPtr&& o) = delete;//禁用移动赋值

	Person* operator->() {
		return p;
	}

	Person& operator*() {
		return *p;
	}

	void reset() {//手动释放内存
		if (p != nullptr) {
			delete p;
			p = nullptr;
		}
	}

	~AutoPersonPtr() {
		if (p != nullptr) {
			delete p;//自动释放指针
		}
	}
};

int main() {
	AutoPersonPtr p1(new Person(1, 12, "jack"));
	AutoPersonPtr p2(2, 18, "jenny");

	p1->show();
	p2->show();

	(*p1).show();

	//手动释放
	//p1.reset();
	//p2.reset();
	cout << "-----main over------" << endl;
	return 0;
}
void reset() {//手动释放内存
	if (p != nullptr) {
		delete p;
		p = nullptr;
	}
}

1.自定义成员工具函数:void reset()

  • 功能:手动提前释放托管堆内存,不用等到智能指针析构;
  • 逻辑:判空p!=nullptr→delete 释放堆对象→指针置空p=nullptr,避免野指针;
  • 特点:析构会二次判空,已经 reset 置空的指针,析构不会重复释放;
  • 调用:p1.reset();
p1->show();
p2->show();

(*p1).show();

2.*/->关键区分

  • ->:运算符重载返回指针,指针用 -> 调函数
  • *:运算符重载返回对象引用,实体对象用。调函数

3.进阶:将自定义的智能指针提升为类模板

template<class T>
class UniPtr {
    T* ptr;
public:
    UniPtr(T* ptr) : ptr(ptr) {}
    ~UniPtr() {
        if (ptr != nullptr) delete ptr;
    }
    T* operator->() { return ptr; }
    T& operator*() { return *ptr; }
    void reset() {
        if (ptr != nullptr) {
            delete ptr;
            ptr = nullptr;
        }
    }
    T& get() {
        return *ptr;
    }
};

三、四种类型的智能指针

前提:定义在<memory>头文件中

先说一下auto_ptr的缺陷

  • auto_ptr拷贝构造 / 赋值时,资源所有权直接夺走,原对象变成空指针(核心缺陷

  • 不支持数组:auto_ptr<T[]>非法,不能管理 new [] 开辟的数组内存,析构只用delete不用delete[],数组内存泄漏。

  • 缺少const限制:const auto_ptr<T>只能锁住指针变量不能改指向,但可以通过 get () 修改堆里数据,设计不严谨。

  • 赋值重载隐患:自赋值会释放自身资源,产生未定义行为。

等等问题...

1.unique_ptr

1.1 语法

方式1:new初始化

unique_ptr<Person> p1(new Person(1,20,"张三"));

方式2:C++14推荐 make_unique(优先使用,安全)

auto p2 = make_unique<Person>(2,22,"李四");

不允许拷贝,只允许移动所有权!

unique_ptr<Person> p3 = p1; //特点是独占所有权
auto p3 = move(p1); //p1变空,资源转给p3
1.2 常用成员函数
函数 作用
.get() 返回内部裸指针 Person*
.reset(裸指针) 释放原有资源,接管新指针;无参直接释放、置空
.release() 交出资源所有权,返回裸指针,unique_ptr 自身置空(不会 delete
.swap(up) 交换两个 unique_ptr 托管资源
.operator->() p->show()重载箭头
.operator*() (*p).show()解引用
.bool() if(p):指针非空 true,空则 false
1.3 核心特点
  • 独占所有权:同一时间,只有一个unique_ptr能指向某块堆内存,不允许拷贝,只支持移动
  • 轻量级:无额外开销(无引用计数),效率接近普通指针
  • 自动释放:析构时释放指向的内存,也可以手动调reset()释放
#include<memory>
int main() {
	unique_ptr<int>p1(new int(88));
	cout << p1.get() << endl;

	//p1.reset();//直接回收

	int *src_p1=p1.release();//返回原指针,需要自己回收
	delete src_p1;

	unique_ptr<Person>p2(new Person(1008, 21, "jyyy"));
	cout << p2.get() << endl;

	p2->show();
	(*p2).show();

	Person* person = new Person(1009, 22, "jyyy");
	unique_ptr<Person>up(person);
	up->show();

	//因为unique_ptr是独占式
	//同一个指针不能放在两个智能指针对象中
	//以下代码则报异常
	unique_ptr<Person>up2(person);
	up2->show();

    // 可以移动
    unique_ptr<Person> up3 = move(up);
    up3->show();
    
	return 0;
}
1.4 适用场景
  • 绝大多数单个对象资源管理(日常开发首选),函数内临时堆对象、类成员资源;
  • auto p = make_unique<Person>(1,18,"张三");
  • 容器存放堆对象vector<unique_ptr<Person>>,容器销毁自动释放全部元素,不会内存泄漏;
  • 作为函数返回值:函数返回unique_ptr,编译器优化隐式移动,安全转交资源;
  • 禁止多指针共用同一块内存,资源只允许一个管理者。
不适合

需要多个指针同时共用同一个堆对象。

2.shared_ptr

2.1 定义语法
​//写法1
shared_ptr<Person> s1(new Person(1,18,"A"));
//写法2 推荐 make_shared(效率高)
auto s2 = make_shared<Person>(2,19,"B");

//允许拷贝,计数+1
shared_ptr<Person> s3 = s2; //s2、s3共用同一块内存,引用计数=2

原理:堆上额外存引用计数器,每多一个 shared_ptr 指向,count++;析构 count--,count==0 释放堆对象。

2.2 常用成员函数
函数 功能
.use_count() 返回当前引用计数值(int)
.unique() 返回 bool:count==1为 true,否则 false
.get() 获取原生裸指针
.reset() 无参:计数 - 1、清空当前指针;带参替换托管对象
.swap(sp) 交换资源
->* 正常重载,访问成员
2.3 核心特性
  • 共享所有权:多个shared_ptr指针可指向同一块内存
  • 引用计数
  • 线程安全
int main() {
    shared_ptr<Person> p1(new Person(1008, "Jack", 21));

    if (p1.unique()) {
        cout << "p1 即是 unique_ptr" << endl;
    }

    p1->show();

    {
        // 多个共享智能指针,同时指向同一个堆内容(数据)
        shared_ptr<Person> p2 = p1;
        shared_ptr<Person> p3 = p1;
        shared_ptr<Person> p4 = p1;

        p2->show();
        p3->show();
        p4->show();

        cout << "inblock 引用计数:  " << p1.use_count() << endl;
    }
    cout << "outblock 引用计数:  " << p1.use_count() << endl;

    p1.reset();
    cout << "over 引用计数:  " << p1.use_count() << endl;

    return 0;
}
2.4 适用场景
  • 多个对象共享同一份资源:多个类成员、多个函数同时持有同一个堆资源;
  • 跨函数、跨线程共享资源:多线程访问同一个对象,用 shared_ptr 保证资源不会提前释放;
  • 不确定资源什么时候销毁,多方共用
不适合

简单独占场景(多余性能损耗)。

3.weak_ptr

不能单独管理内存,只能从 shared_ptr 构造

3.1 定义语法
auto sp = make_shared<Person>(1,20,"小明");
weak_ptr<Person> wp = sp; //绑定shared_ptr,不增加计数

weak_ptr不能直接 ->、*访问对象,必须先升级成 shared_ptr。

3.2 常用成员函数
函数 作用
.lock() 返回shared_ptr<T>,升级为共享指针;资源已释放返回空 shared
.expired() bool:资源已销毁返回 true,可用 false
.reset() 清空 weak_ptr,不再监视资源
.swap(wk) 交换两个 weak_ptr
.use_count() 查看绑定的 shared_ptr 当前引用计数
3.3 核心特性
  • 弱引用:指向shared_ptr管理的内存,但不增加引用计数
  • 不拥有所有权:无法直接访问对象,需要先转换为shared_ptrlock()方法)
  • 解决循环引用:shared_ptr的循环引用会导致计数无法归0,内存泄漏,weak_ptr可以解决
int main() {
    shared_ptr<Person> p = make_shared<Person>(1, "Lucy", 17);
    weak_ptr<Person> wp = p;
    weak_ptr<Person> wp2 = p;

    // weak_ptr 不能直接访问内存空间
    //wp->show();

    // shared_ptr 引用计数为0时,weak_ptr则过期
    // false 未过期,  true 过期(引用计数为0)
    cout << boolalpha << wp.expired() << endl;
    cout << boolalpha << wp2.expired() << endl;

    //p->show();
    wp.lock()->show();
    p.reset();
    cout << boolalpha << wp.expired() << endl;
    cout << boolalpha << wp2.expired() << endl;


    return 0;
}
3.4 适用场景
  1. 解决 shared_ptr 循环引用(最核心用途):A 存 shared_ptr<B>,B 存 weak_ptr,打破环形引用;
  2. 缓存 / 观察者模式:缓存池用 weak_ptr 监听资源,资源被释放后缓存自动失效;
  3. 只想查看资源、不想持有资源生命周期,需要用时lock()提升为 shared_ptr 临时占用;
不适合

单独管理堆内存、独占资源。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐