C++11QT复习 (七)

news2025/4/3 22:37:13

智能指针雏形

    • **Day7-1 智能指针雏形:独占语义与共享语义**
      • **1. 独占语义与共享语义**
        • **1.1 Circle 类:示例类**
      • **2. 拷贝构造:独占语义(Unique Ownership)**
        • **2.1 代码解析**
      • **3. 拷贝构造:共享语义(Shared Ownership)**
        • **3.1 代码解析**
      • **4. 智能指针 std::unique_ptr 和 std::shared_ptr**
        • **4.1 代码解析**
      • **5. 移动语义(Move Semantics)**
        • **5.1 代码解析**
      • **总结**

Day7-1 智能指针雏形:独占语义与共享语义

1. 独占语义与共享语义

在 C++ 中,拷贝构造的语义可以分为独占语义共享语义

  • 独占语义(Unique Ownership):每个对象拥有一块独立的内存,拷贝时进行深拷贝
  • 共享语义(Shared Ownership):多个对象共享同一块内存,拷贝时进行浅拷贝,通常需要引用计数来管理资源。

C++ 标准库提供了 std::unique_ptrstd::shared_ptr 来实现这两种语义。

1.1 Circle 类:示例类

Circle 类是一个普通的对象类,包含构造函数、析构函数、成员变量和方法。

// Circle.h
#pragma once
#include <iostream>
#include <cmath>
using namespace std;

//关键字
//const
const int global_a = 10;
//static
static int s_b = 10;
//extern
extern int SIZE = 10;
//三者的用途:const 修饰常量,static 修饰全局变量,extern 修饰外部变量

class Circle
{
public:
    Circle(double r = 0)
        : _r(r)
    {
        cout << "Circle(double r = 0)" << endl;
    }

    Circle(double r, char* name)
        : _r(0), _name(new char[strlen(name) + 1])
    {
        strcpy_s(_name, strlen(name) + 1, name);
        cout << "Circle(double r, char* name)" << endl;
    }

    ~Circle()
    {
        cout << "~Circle()" << endl;
        delete[] _name;
    }

    double getRadius() const { return _r; }
    void setRadius(double r) { _r = r; }
    double getArea() const { return M_PI * _r * _r; }

private:
    double _r;
    char* _name;
};

2. 拷贝构造:独占语义(Unique Ownership)

UniqueClass 实现了独占语义,即对象的拷贝会创建新的独立对象,保证每个对象都拥有独立的内存。

// ShareClass.h
class UniqueClass
{
    int times = 0;
public:
    UniqueClass(int a)
        : _data(new int(a))
    {
    }
    
    ~UniqueClass()
    {
        delete _data;
        _data = nullptr;
    }

    // 深拷贝构造函数
    //UniqueClass(const UniqueClass& rhs)
	//	:_data(new int(*rhs.data))
	//{
	//	cout << "Call desctructor";
	//	times++;
	//	cout << times << endl;
	//}


	//在这段代码中,rhs 不是指针,而是一个 UniqueClass 类型的引用。
	// *rhs._data 是对 rhs 对象的 _data 成员指针进行解引用。
	UniqueClass(const UniqueClass& rhs)
		: _data(new int(*rhs._data)) // 修复了错误的冒号和成员变量名称
	{
		cout << "Call constructor";
		times++;
		cout << times << endl;
	}

	  /*
		1.	UniqueClass(const UniqueClass & rhs) 是 UniqueClass 的拷贝构造函数。它接受一个 UniqueClass 类型的常量引用 rhs 作为参数。
		2.	: _data(new int(*rhs._data)) 是成员初始化列表的一部分。它的作用是初始化 _data 成员变量。
		3.	rhs._data 是对 rhs 对象的 _data 成员指针的访问。
		4. * rhs._data 是对 rhs._data 指针的解引用,获取指针指向的整数值。
		5.	new int(*rhs._data) 创建了一个新的 int 对象,并将 * rhs._data 的值复制到这个新的 int 对象中。然后将新创建的 int 对象的指针赋值给 _data 成员变量。
		这样做的目的是在拷贝构造函数中为新对象分配一个新的内存空间,并将原对象的 _data 成员
		指针指向的值复制到新对象的 _data 成员指针指向的内存中。这确保了每个 UniqueClass 对象
		都有自己独立的 _data 内存空间,避免了多个对象共享同一块内存,从而防止潜在的内存管理问题。
		*/


	/*传入的参数可以修改,修改后不可以再使用,最好当成右值使用*/
	//移动构造函数的写法(C++11 移动构造函数,接受右值)
		// 拷贝指针,并断掉源对象对内存的引用(置空)
		// 如果传入参数本身是右值,没有任何副作用,因为调用结束,它就不在了
		// 但传入的如果是个左值转换来的右值,那要注意,充当了右值,右值的语义就是临时的,
		// 转瞬即时的,所以这个左值也应看成已逝的事物,不可以再使用
	UniqueClass(UniqueClass&& rhs)
		:_data(rhs._data)
	{
		rhs._data = nullptr;
	}

	// C++11 移动赋值运算符,接受右值,和移动构造同理
	UniqueClass& operator=(UniqueClass&& rhs)
	{
		_data = rhs._data;
		rhs._data = nullptr;
	}

private:
    int* _data;
};
2.1 代码解析
  • 拷贝构造函数:创建新对象时,为 _data 申请新的内存,保证对象的独占性。
  • 移动构造函数:从右值拷贝指针,并将原对象的指针置空,避免重复释放内存。
  • 移动赋值运算符:清理当前对象的内存,然后从右值拷贝指针,并清空右值的指针。

3. 拷贝构造:共享语义(Shared Ownership)

SharedClass 采用引用计数管理共享的内存。

class SharedClass
{
public:
    static int count; // 静态引用计数
    int* _data;

    SharedClass(int a) : _data(new int(a)) {}
	// 拷贝构造,拷贝指针,增加引用计数
    SharedClass(const SharedClass& r)
        : _data(r._data)
    {
        count++;
    }
    
    // 拷贝赋值,拷贝指针,增加引用计数
    SharedClass& operator=(const SharedClass& r)
    {
        if (this != &r)
        {
            _data = r._data;
            count++;
        }
        return *this;
    }

    // 析构时注意减少引用计数,归0时释放堆内存
    ~SharedClass()
    {
        --count;
        if (count == 0)
        {
            delete _data;
        }
    }
};

int SharedClass::count = 1;
3.1 代码解析
  • 引用计数 count:确保多个对象共享同一块内存。
  • 拷贝构造函数:增加引用计数。
  • 析构函数:当 count == 0 时,释放 _data

4. 智能指针 std::unique_ptr 和 std::shared_ptr

C++ 提供 std::unique_ptrstd::shared_ptr,分别对应独占语义和共享语义。

void testSmartPointer()
{
    std::unique_ptr<int> unique1 = std::make_unique<int>(10);
    // std::unique_ptr<int> unique2 = unique1; // 错误:无法复制 unique_ptr

    std::shared_ptr<int> share1 = std::make_shared<int>(10);
    std::shared_ptr<int> share2 = share1;
    cout << "share2 use_count() = " << share2.use_count() << endl;
}
4.1 代码解析
  • std::unique_ptr 禁止拷贝,确保对象独占内存。
  • std::shared_ptr 可以拷贝,使用引用计数管理内存。

5. 移动语义(Move Semantics)

void testMoveSemantic()
{
	UniqueClass u1(10);
	cout << "u1.data = " << u1._data << endl; 
	cout << "*(u1)._data = " << * (u1)._data << endl; 

	//UniqueClass u2 = (UniqueClass&&)u1;
	//等价于
	UniqueClass u2 = std::move(u1);// move将uc1这个左值转为右值,即(UniqueClass&&)uc1;
	cout << "u2.data = " << u2._data << endl;
	cout << "*(u2)._data = " << *(u2)._data << endl;


	if (u1._data = nullptr)
	{
		cout << "u1 is gone " << endl;
	}
}
5.1 代码解析
  • std::move(u1)u1 变为右值,调用移动构造函数
  • u1._data = nullptr,原对象 u1 失效。

总结

方式语义内存管理适用场景
std::unique_ptr独占不能拷贝资源独占,如文件句柄
std::shared_ptr共享计数管理共享资源,如缓存
拷贝构造独占/共享深拷贝/浅拷贝具体需求
移动构造独占资源转移避免临时对象开销

通过这些方法,我们可以更高效地管理 C++ 中的资源分配和释放,避免内存泄漏和重复释放。

深拷贝(独占)

  • 拷贝的是 ,拷贝后 新对象与源对象完全独立
  • 析构一方 不影响 另一方。

浅拷贝(共享)

  • 拷贝的是 指针/引用,拷贝后 新对象与源对象共享同一份内存
  • 析构一方后 另一方会受到影响

为了防止 重复 delete,需要引入 引用计数,只有当 没有任何对象引用这块堆内存时,才释放它

std::shared_ptr 实现的思路:

std::shared_ptr 通过 内部的引用计数 记录有多少个 shared_ptr 共享同一块内存,只有当 引用计数归零时,才释放资源

#include <iostream>
#include <memory>

using namespace std;

void sharedPtrDemo()
{
	shared_ptr<int> p1 = make_shared<int>(10);
	{
		shared_ptr<int> p2 = p1;
		cout << "引用计数: " << p1.use_count() << endl;
	} // p2 作用域结束,引用计数减少
	cout << "引用计数: " << p1.use_count() << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2326580.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32八股【5】----- TIM定时器

1. TIM定时器分类 STM32 的定时器主要分为以下几类&#xff1a; 高级定时器&#xff08;Advanced TIM&#xff0c;TIM1/TIM8&#xff09; 具备 PWM 生成、死区控制、互补输出等高级功能&#xff0c;适用于电机控制和功率转换应用。通用定时器&#xff08;General-purpose TIM…

厘米级定位赋能智造升级:品铂科技UWB技术驱动工厂全流程自动化与效能跃升”

在智能制造中的核心价值体现在‌高精度定位、流程优化、安全管理‌等多个维度&#xff0c;具体应用如下&#xff1a; 一、‌核心技术与定位能力‌ ‌厘米级高精度定位‌ UWB技术通过‌纳秒级窄脉冲信号‌&#xff08;带宽超500MHz&#xff09;实现高时间分辨率&#xff0c;结合…

C++刷题(四):vector

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及刷题记录&#xff0c;使用语言为C。 每道题我会给出LeetCode上的题号&#xff08;如果有题号&#xff09;&#xff0c;题目&#xff0c;以及最后通过的代码。没有题号的题目大多来自牛客网。对于题目的…

【虚拟仪器技术】Labview虚拟仪器技术应用教程习题参考答案[13页]

目录 第1章 第2章 第3章 第4章 第&#xff15;章 第&#xff16;章 第&#xff17;章 第8章 第1章 1. 简述虚拟仪器概念。 参考答案&#xff1a;虚拟仪器是借助于强大的计算机软件和硬件环境的支持&#xff0c;建立虚拟的测试仪器面板&#xff0c;完成仪器的控制、数…

UE5学习笔记 FPS游戏制作34 触发器切换关卡

文章目录 搭建关卡制作触发器传送门显示加载界面 搭建关卡 首先搭建两个关卡&#xff0c;每个关卡里至少要有一个角色 制作触发器传送门 1 新建一个蓝图&#xff0c;父类为actor&#xff0c;命名为portal&#xff08;传送门&#xff09; 2 为portal添加一个staticMesh&#…

智谱大模型(ChatGLM3)PyCharm的调试指南

前言 最近在看一本《ChatGLM3大模型本地化部署、应用开发和微调》&#xff0c;本文就是讨论ChatGLM3在本地的初步布设。&#xff08;模型文件来自魔塔社区&#xff09; 1、建立Pycharm工程 采用的Python版本为3.11 2、安装对应的包 2.1、安装modelscope包 pip install model…

新专栏预告 《AI大模型应知应会短平快系列100篇》 - 整体规划设计

做个预告&#xff0c;为系统化梳理AI大模型的发展脉络&#xff0c;并为普及AI素养做一点贡献&#xff0c;特给自己制定了一个小目标&#xff0c;3个月内完成交稿。 AI大模型应知应会短平快系列100篇 - 整体规划设计 一、基础知识模块&#xff08;20篇&#xff09; 1.1 大模型…

SwanLab Slack通知插件:让AI训练状态同步更及时

在AI模型训练的过程中&#xff0c;开发者常常面临一个难题&#xff1a;如何及时跟踪训练状态&#xff1f;无论是实验超参数的调整、关键指标的变化&#xff0c;还是意外中断的告警&#xff0c;传统的监控方式往往依赖手动刷新日志或反复检查终端&#xff0c;这不仅效率低下&…

操作系统高频(六)linux内核

操作系统高频&#xff08;六&#xff09;linux内核 1.内核态&#xff0c;用户态的区别⭐⭐⭐ 内核态和用户态的区别主要在于权限和安全性。 权限&#xff1a;内核态拥有最高的权限&#xff0c;可以访问和执行所有的系统指令和资源&#xff0c;而用户态的权限相对较低&#x…

位置编码汇总 # 持续更新

看了那么多还没有讲特别好的&#xff0c;GPT老师讲的不错关于三角函数编码。 一、 手撕transformer常用三角位置编码 GPT说&#xff1a;“低维度的编码&#xff08;例如&#xff0c;第一个维度&#xff09;可以捕捉到大的位置差异&#xff0c;而高维度的编码则可以捕捉到小的细…

DaVinci Resolve19.1下载:达芬奇调色中文版+安装步骤

如大家所了解的&#xff0c;DaVinci Resolve中文名为达芬奇&#xff0c;是一款专业视频编辑与调色软件。它最初以调色功能闻名&#xff0c;但经过多年发展&#xff0c;已扩展为一套完整的后期制作解决方案&#xff0c;涵盖了剪辑、视觉特效、动态图形和音频后期制作等多个模块。…

LINUX 1

快照 克隆&#xff1a;关机状态下&#xff1a;长时间备份 uname 操作系统 -a 获取所有信息 绝对路径 相对路径 -a -l 列表形式查看 -h 查看版本 相对路径这个还没太搞懂 LS -L LL 简写 显示当前路径 pwd cd 切换到目录 clear 清屏 reboot 重启操作系统

高效定位 Go 应用问题:Go 可观测性功能深度解析

作者&#xff1a;古琦 背景 自 2024 年 6 月 26 日&#xff0c;阿里云 ARMS 团队正式推出面向 Go 应用的可观测性监控功能以来&#xff0c;我们与程序语言及编译器团队携手并进&#xff0c;持续深耕技术优化与功能拓展。这一创新性的解决方案旨在为开发者提供更为全面、深入且…

【Windows】win10系统安装.NET Framework 3.5(包括.NET 2.0和3.0)失败 错误代码:0×80240438

一、.NET3.5(包括.NET 2.0和3.0)安装方式 1.1 联网安装(需要联网,能访问微软,简单,很可能会失败) 1.2 离线安装-救急用(需要操作系统iso镜像文件,复杂,成功几率大) 二、联网安装 通过【控制面板】→【程序】→【程序和功能】→【启用或关闭Windows功能】 下载过程…

蓝桥杯训练士兵

思路&#xff1a;其实每次就是要比较士兵单独训练的价格之和SUM与S的大小&#xff0c;如果 SUM大&#xff0c;那么就减去所有士兵都要训练的次数的最小值&#xff0c;SUM再更新一下&#xff0c;继续比较。 先对士兵的次数按从小到大的次序排序&#xff08;很重要&#xff09;&…

循环神经网络 - 简单循环网络

本文我们来学习和了解简单循环网络(Simple Recurrent Network&#xff0c;SRN)&#xff0c; SRN是一个非常简单的循环神经网络&#xff0c;只有一个隐藏层的神经网络。 简单循环神经网络&#xff0c;也常称为 Elman 网络&#xff0c;是最基本的循环神经网络&#xff08;RNN&am…

Linux 企业项目服务器组建(附脚本)

一、架构概述​ 本方案旨在为企业搭建一套高效、安全的 Linux 服务器架构&#xff0c;包含一台 DNS 服务器&#xff0c;以及一台同时承载 FTP 和 Samba 服务的服务器&#xff0c;满足公司在域名解析、图片存储与共享、文件共享等方面的业务需求。​ 二、服务器部署​ DNS 服…

⼆、Kafka客户端消息流转流程

这⼀章节将重点介绍Kafka的HighLevel API使⽤&#xff0c;并通过这些API&#xff0c;构建起Kafka整个消息发送以及消费的主线流程。 Kafka提供了两套客户端API&#xff0c;HighLevel API和LowLevel API。 HighLevel API封装了kafka的运⾏细节&#xff0c;使⽤起来⽐较简单&…

es 3期 第28节-深入掌握集群组建与集群设置

#### 1.Elasticsearch是数据库&#xff0c;不是普通的Java应用程序&#xff0c;传统数据库需要的硬件资源同样需要&#xff0c;提升性能最有效的就是升级硬件。 #### 2.Elasticsearch是文档型数据库&#xff0c;不是关系型数据库&#xff0c;不具备严格的ACID事务特性&#xff…