IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++【内存管理】| 【智能指针】动态内存管理 -> 正文阅读

[C++知识库]C++【内存管理】| 【智能指针】动态内存管理

一、简介

是一种抽象类型,由类模板来实现,借由类别的析构函数来达到自动释放指针所指向的存储器或对象;

  • 支持自动、异常安全的对象生命周期管理;

二、四种智能指针

1、auto_ptr

该智能指针管理通过new创建auto_ptr获得的对象,并在其自身被销毁时删除该对象

注意

  • 不可用多个指向同一个对象,当使用拷贝构造赋值运算符时,右值会变为nullptr
  • 在C++11被弃用,C++17被删除;

2、unique_ptr

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

通过指针拥有和管理对象,且对该对象具有唯一权,没有其他操作能删除该对象;

  • 该智能指针禁止拷贝赋值操作,但可通过std::move移动赋值;
  • 可通过reset进行更改;
  • 对于数组对象提供[]运算符;

关于为何对接管对象具有唯一权

由于当该对象被删除时,若有其他指针托管该对象,则其他的指针将会无效;

2.1 测试案例

#include <iostream>
#include <memory>
#include <string>

using namespace std;


class A{
public:
	A(int a) : m_a(a) {
		cout << "A..." << endl;
	}

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

	int getA() const {
		return m_a;
	}

private:
	int m_a;
};

创建

void test_unique_ptr() {

	// 方式1 
	std::unique_ptr<A> u_a(new A(1));
	// 方式2
	std::unique_ptr<A> u_b;
	u_b.reset(new A(2));
	// 方式3
	std::unique_ptr<A> u_c = std::make_unique<A>(3);
	cout << "u_a: " << u_a->getA() << endl;
	cout << "u_b: " << u_b->getA() << endl;
	cout << "u_c: " << u_c->getA() << endl;

	// std::move
	std::unique_ptr<A> u_d = std::move(u_c);
	cout << "u_d : " << u_d ->getA() << endl;	
}

在这里插入图片描述
reset

void test_unique_ptr() {

	std::unique_ptr<A> u_b(new A(2));
	u_b.reset(new A(4));	// 会先释放内存

	cout << "u_b: " << u_b->getA() << endl;
}

在这里插入图片描述

3、weak_ptr

template <class T> class weak_ptr;

弱管理对象的类型,提供了对其管理的资源的一个访问手段,为协助shared_ptr

  • 析构和构造不会造成计数的增加或减少;
  • 可以解决两个shared_ptr互相引用造成的死锁问题,导致资源不会释放;
  • 该智能指针没有重载*以及->,故不能直接操作对象,由于可能在操作的时候该对象被释放而引发错误;

3.1 提供的成员函数

operator=

  • 不会获得所有权,且不会增加计数;
  • shared_ptr可以直接分配给weak_ptr
  • 当一个weak_ptr分配给shared_ptr需要使用lock来完成;
void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2;
	std::weak_ptr<A> wptr;

	// 将shared_ptr转换为weak_ptr
	wptr = ptr1;

	// 将weak_ptr转换为shared_ptr
	ptr2 = wptr.lock();

	cout << "ptr1:" << &(*ptr1) << endl;
	cout << "ptr2:" << &(*ptr2) << endl;
}

swap

交换对象;

void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2(new A(2));
	std::weak_ptr<A> wptr1 = ptr1;
	std::weak_ptr<A> wptr2 = ptr2;

	wptr1.swap(wptr2);
	cout << "ptr1: " << &(*ptr1) << endl;
	cout << "ptr2: " << &(*ptr2) << endl;
	cout << "wptr1: " << &(*wptr1.lock()) << endl;
	cout << "wptr2: " << &(*wptr2.lock()) << endl;
}

reset

重置变为空;

void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::weak_ptr<A> wptr1 = ptr1;
	wptr1.reset();

	std::cout << "wptr1 " << (wptr1.expired() ? "" : "没有") << " 过期\n";
}

use_count

共享所有权的shared_ptr对象的数量

  • weak_ptr不计算在内;

expired

  • 检查是否过期, weak_ptr对象是否为空;

lock

若未过期,返回一个shared_ptr,包含由weak_ptr对象保留的信息;

3.2 应用场景

  • 辅助shared_ptr的使用;
  • 出现于该资源能使用则使用,不能使用则不用;

4、shared_ptr

template <class T> class shared_ptr;

对象具有获得指针所有权并共享该所有权的能力,持有的资源能在多个shared_ptr之间共享;

  • 当多一次对该对象的引用,则引用计数加一,若发生析构,则减一;
  • 当引用计数为0时,则释放该资源;

4.1 常用成员函数

operator=

  • 复制:使该对象的引用计数加1;
  • 移动:则原对象将变为空;
void test_shared_ptr() {
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2;

	// copy
	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;
	ptr2 = ptr1;

	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;

	// move
	ptr2 = std::make_shared<A>(2);
	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;
}

swap

交换托管对象内容,不会破坏或更改引用计数;

void test_shared_ptr() {
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2(new A(2));

	ptr1.swap(ptr2);
	cout << "ptr1引用计数: " << ptr1.use_count() << endl;
	cout << "ptr2引用计数: " << ptr2.use_count() << endl;
}

reset

重置指针,若原先由托管,则会将原先的删除;

  • 若不传入参数,则原对象的引用计数会减一;
  • 若传入参数,则该对象的引用计数会加一;
void test_shared_ptr() {
	std::shared_ptr<A> ptr1(new A(1));

	ptr1.reset();
	cout << "ptr1引用计数: " << ptr1.use_count() << endl;
	ptr1.reset(new A(2));
	cout << "ptr1引用计数: " << ptr1.use_count() << endl;
}

get

获取指针;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << "ptr1:" << ptr1.use_count() << endl;

	if (a == ptr1.get()) {
		cout << "equal..." << endl;
	}
}

operator*

取消引用对象;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << "ptr1:" << ptr1.use_count() << endl;
	
	if (a == ptr1.get()) {
		cout << "equal..." << endl;
	}

	if (&(*ptr1) == a){
		cout << "equal..." << endl;
	}
}

operator->

取消引用对象成员,即直接调用对象成员;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << a->getA() << endl;
}

use_count

获取引用计数;

unique

检查是否唯一;

operator bool

检查是否不为空;

operators ==, !=, <, <=, >, >=

重载了运算符;

operator<<

make_shared

制作shared_ptr;

4.2 引用计数

当计数器为0时,智能指针将会自动释放

何时计数器会递增

  • 当使用拷贝时shared_ptr<T> p(q)p时q的拷贝,计数器会递增;
  • 当两个智能指针相互转换时,左边的指针对象会增加
  • 当它作为参数传递时;
  • 当它作为函数返回值时;

何时计数器会递减

  • 当两个智能指针相互转换时,右边的指针对象会递减
  • 当智能指针被销毁时;

使用new返回的指针来初始化智能指针;

  • 一般初始化智能指针的指针必须指向动态内存;
  • 由于之智能指针内部的构造函数是explicit,不能进行隐式转换,必须使用直接初始化
  • 由于不清楚对象何时销毁,则最好不使用内置指针来访问一个智能指针且不使用get()初始化或赋值;
shared_ptr<T> p(new T(1));

4.3 share_ptr与new结合使用

当new与share_ptr作为参数时

// 函数声明
int func();
void test(std::shared_ptr<int> p, int func);

由于智能指针内部的构造函数是explicit,故不能直接传入new创建的指针;

// 可考虑使用以下:
test(std::shared_ptr<int> (new int), func());

第一个参数被分为俩部分:std::shared_ptr<int>new int
由于编译器考虑高效的操作,故传入参数的执行顺序:

  • 执行new int
  • 调用func
  • 调用智能指针构造

但需要考虑到,万一func函数内部出现异常,则此时将会引起内存泄漏,由于new int返回的指针遗失;


故将实参一先单独出来:

std::shared_ptr<int> p(new int);
test(p, func());

4.4 enable_shared_from_this

在子类中继承该类,能够让子类的对象创建指向自身的shared_ptr实例;

  • 获取返回会增加引用计数;
class A : enable_shared_from_this<A>{
public:
	A(int a) : m_a(a) {
		cout << "A..." << endl;
	}

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

	shared_ptr<A> GetThis() {
		return shared_from_this();
	}

	int getA() const {
		return m_a;
	}

private:
	int m_a;
};


void test_shared_ptr() {
	std::shared_ptr<A> ptr(new A(1));

	auto p = ptr->GetThis();
	cout << ptr.use_count() << endl;
}

注意事项

  • 不能将栈对象,来获取shared_from_this,会导致程序奔溃;
void test_shared_ptr() {
	A a(1);
	auto p = a.GetThis();
}

5、注意事项

  • 分清楚什么情况下使用哪一种指针:
    • 当该资源只用在当前且不共享的情况下,使用unique_ptr
    • 当智能指针不需要管理对象的生命周期下,使用weak_ptr
    • 若要其他地方共享使用,使用shared_ptr
  • 注意使用时,该资源是否有效
  • 作为类成员的时,一般优先考虑向前声明,而不是作为头文件导入;
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:05:39  更:2022-04-09 18:08:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:56:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码