当前位置: 首页 > news >正文

C++之智能指针

前言

普通指针的不足

new和new门的内存需要用delete和delete[释放。
程序员的主观失误,忘了或漏了释放
程序员也不确定何时释放(例如多个线程共享同一个对象,没办法确定什么时候释放)

普通指针的释放

类内的指针,在析构函数中释放
C++内置数据类型,如何释放?
new出来的类,本身如何释放?

智能指针设计思路

智能指针是类模板,在栈上创建智能指针对象
把普通指针交给智能指针对象
智能指针对象过期时,调用析构函数释放普通指针的内存

什么是智能指针

智能指针是RALL机制对普通指针进行的一层封装。这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以方便管理一个对象的生命周期。

智能指针详解

unique_ptr

定义:unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一个对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。

头文件:#include<memory>

template <typename T, typename D = default_delete<T>>
class unique_ptr
{
public:explicit unique_ptr(pointer p) noexcept;    // 不可用于转换函数。~unique_ptr() noexcept;    T& operator*() const;            // 重载*操作符。T* operator->() const noexcept;  // 重载->操作符。unique_ptr(const unique_ptr &) = delete;   // 禁用拷贝构造函数。unique_ptr& operator=(const unique_ptr &) = delete;  // 禁用赋值函数。unique_ptr(unique_ptr &&) noexcept;      // 右值引用。unique_ptr& operator=(unique_ptr &&) noexcept;  // 右值引用。// ...
private:pointer ptr;  // 内置的指针。
};

下面进行测试

#include<memory>
#include<iostream>
#include<vector>
using namespace std;
class AA
{
public:string m_name;AA() { cout << m_name << "调用构造函数AA()。" << endl;}AA(const string& name) : m_name(name) {cout << "调用构造函数AA(" << m_name << ")。" << endl;}~AA() { cout << "调用了析构函数~AA(" << m_name << ")。" << endl;}
};

测试1

void test1()
{AA* p = new AA("张三");//delete p;    //这里结社我们忘记delete
}

运行结果:

调用构造函数AA(张三)。

我们发现出现了内存泄漏问题,这里我们是故意忘记释放,但是真正到了开发时,都有可能会忘记释放,那怎么办呢?我们可以用智能指针unique_ptr

测试2

void test2()
{AA* p = new AA("张三");unique_ptr<AA> p1(p);
}

运行结果

调用构造函数AA(张三)。
张三调用了析构函数~AA(张三)。

我们惊奇的发现我们没有调用delete,但是也调用了析构函数,因为智能指针是一个类,它有析构函数,在析构函数中有delete语句。

并且类重载了->和*,可以像使用普通指针一样使用智能指针

测试3

void test3()
{AA* p = new AA("张三");unique_ptr<AA> p1(p);cout << (*p1).m_name << endl;cout << p1->m_name << endl;cout << p->m_name << endl;cout << (*p).m_name << endl;
}

运行结果

调用构造函数AA(张三)。
张三
张三
张三
张三
张三调用了析构函数~AA(张三)。

基本用法

初始化

//方法1
unique_ptr<AA> p0(new AA("西施"));     // 分配内存并初始化
//方法2
unique_ptr<AA> p0 = make_unique<AA>("西施");   // C++14标准。
unique_ptr<int> pp1=make_unique<int>();         // 数据类型为int。
unique_ptr<AA> pp2 = make_unique<AA>();       // 数据类型为AA,默认构造函数。
unique_ptr<AA> pp3 = make_unique<AA>("西施");  // 数据类型为AA,一个参数的构造函数。
unique_ptr<AA> pp4 = make_unique<AA>("西施",8); // 数据类型为AA,两个参数的构造函数。
//方法3
AA* p = new AA("西施");
unique_ptr<AA> p0(p);                  // 用已存在的地址初始化。

注意:

  1. 重载了->和*,可以像使用普通指针一样使用unique_ptr

  1. 不支持普通的拷贝和赋值

explicit unique_ptr(pointer p) noexcept; // 因为有explicit,所以不可用于转换函数。
unique_ptr(const unique_ptr &) = delete; // 禁用拷贝构造函数
unique_ptr& operator=(const unique_ptr &) = delete; // 禁用赋值函数。

例如以下测试错误的

void test4()
{AA* p = new AA("张三");unique_ptr<AA> p1 = p;    //错误,不能将普通指针直接复制给智能指针unique_ptr<AA>p2 = new AA("李四");    //错误,不能将普通指针直接复制给智能指针unique_ptr<AA> p3 = pu1;   // 错误,不能用其它unique_ptr拷贝构造unique_ptr<AA> p4;p4=p1;    //错误,不能用=对unique_ptr进行赋值
}

为什么要禁用赋值函数呢?

这个与智能指针实现初衷有关,unique_ptr实现的是一个指针独享一份资源,如果可以复制的话,那个就会出现多个unique_ptr指针指向同一块资源,就会出现非法操作野指针。
  1. 不要用同一个裸指针初始化多个unique_ptr对象(也是出现非法操作野指针问题);

  1. 不要用unique_ptr管理不是new分配的内存;

  1. get()方法返回裸指针;

  1. 用于函数的参数只能传引用;

  1. 不支持指针运算(+、-、++、--)。

技巧

1. 将一个unique_ptr赋给另一个时,如果原unique_ptr是一个临时右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器禁止这样做。一般用于函数的返回值;
2. 用nullptr给unique_ptr赋值将释放对象,空的unique_ptr==nullptr;
3. release()释放对原始指针的控制权,将unique_ptr置为空,返回裸指针。(可用于把unique_ptr传递给子函数,子函数将负责释放对象);
4. std::move()可以转移对原始指针的控制权。(可用于把unique_ptr传递给子函数,子函数形参也是unique_ptr);
5. reset()释放对象;
6. swap()交换两个unique_ptr的控制权;
7. unique_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样;
8. unique_ptr不是绝对安全,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放;
9. unique_ptr提供了支持数组的具体化版本。数组版本的unique_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。

这里只演示第1、4、5、8、9;

示例 1:

unique_ptr<AA> func()
{unique_ptr<AA> pp(new AA("小谢"));return pp;
}
void test4()
{unique_ptr<AA> p1(new AA("张三"));unique_ptr<AA> p2;//p2 = p1;p2 = unique_ptr<AA>(new AA("李四"));cout << "调用func之前" << endl;p2 = func();cout << "调用func之后" << endl;
}

示例 4

void func(unique_ptr<AA> a)
{cout << a->m_name << endl;
}
void test5()
{unique_ptr<AA>pu(new AA("张三"));cout << "开始调用函数" << endl;func(move(pu));cout << "调用函数结束" << endl;
}

这里函数调用时使用了move函数,将pu的管理权交给形参

示例 5

void reset(T * _ptr= (T *) nullptr);
pp.reset(); // 释放pp对象指向的资源对象。
pp.reset(nullptr); // 释放pp对象指向的资源对象
pp.reset(new AA("bbb")); // 释放pp指向的资源对象,同时指向新的对象。

示例 8

unique_ptr<AA> p(new AA("全局"));
int main()
{unique_ptr<AA> p1(new AA("局部"));exit(0);
}

结果

调用构造函数AA(全局)。
调用构造函数AA(局部)。
全局调用了析构函数~AA(全局)。

示例 9

unique_ptr<int[]> parr1(new int[3]);          // 不指定初始值。
unique_ptr<int[]> parr1(new int[3]{ 33,22,11 });  // 指定初始值。
cout << "parr1[0]=" << parr1[0] << endl;
cout << "parr1[1]=" << parr1[1] << endl;
cout << "parr1[2]=" << parr1[2] << endl;

shared_ptr

定义:

shared_ptr共享它指向的对象,多个shared_ptr可以指向(关联)相同的对象,在内部采用计数机制来实现。
当新的shared_ptr与对象关联时,引用计数增加1。
当shared_ptr超出作用域时,引用计数减1。当引用计数变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象。

基本用法

初始化

shared_ptr的构造函数也是explicit,但是,没有删除拷贝构造函数和赋值函数。
//方法一:
shared_ptr<AA> p0(new AA("西施"));     // 分配内存并初始化。
//方法二:
shared_ptr<AA> p0 = make_shared<AA>("西施");  // C++11标准,效率更高。
shared_ptr<int> pp1=make_shared<int>();         // 数据类型为int。
shared_ptr<AA> pp2 = make_shared<AA>();       // 数据类型为AA,默认构造函数。
shared_ptr<AA> pp3 = make_shared<AA>("西施");  // 数据类型为AA,一个参数的构造函数。
shared_ptr<AA> pp4 = make_shared<AA>("西施",8); // 数据类型为AA,两个参数的构造函数。
//方法三:
AA* p = new AA("西施");
shared_ptr<AA> p0(p);                  // 用已存在的地址初始化。
//方法四:
shared_ptr<AA> p0(new AA("西施")); 
shared_ptr<AA> p1(p0);                 // 用已存在的shared_ptr初始化,计数加1。
shared_ptr<AA> p1=p0;                 // 用已存在的shared_ptr初始化,计数加1

注意:

  1. 智能指针重载了*和->操作符,可以像使用指针一样使用shared_ptr;

  1. use_count()方法返回引用计数器的值;

  1. unique()方法,如果use_count()为1,返回true,否则返回false

  1. shared_ptr支持赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1(当某个资源没有指向时就会被释放);

  1. get()方法返回裸指针;

  1. 不要用同一个裸指针初始化多个shared_ptr;

  1. 不要用shared_ptr管理不是new分配的内存;

  1. 用于函数的参数只能传引用;

  1. 不支持指针运算(+、-、++、--)。

技巧

1. 用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空shared_ptr==nullptr;
2. std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr;
3. reset()改变与资源的关联关系;
pp.reset(); // 解除与资源的关系,资源的引用计数减1。
pp. reset(new AA("bbb")); // 解除与资源的关系,资源的引用计数减1。关联新资源。
4. swap()交换两个shared_ptr的控制权;
void swap(shared_ptr<T> &_Right);
5. shared_ptr也可象普通指针那样,当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样;
6. shared_ptr不是绝对安全,如果程序中调用exit()退出,全局的shared_ptr可以自动释放,但局部的shared_ptr无法释放;
7. shared_ptr提供了支持数组的具体化版本;
数组版本的shared_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用;
8. shared_ptr的线程安全性:
shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。
多个线程同时读同一个shared_ptr对象是线程安全的。
如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。
9. 如果unique_ptr能解决问题,就不要用shared_ptr。unique_ptr的效率更高,占用的资源更少。

weak_ptr

定义

weak_ptr 是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。
weak_ptr更像是shared_ptr的助手而不是智能指针

这里举个例子

#include <iostream>
#include <memory>
using  namespace std;
class BB;
class AA
{
public:string m_name;AA() { cout << m_name << "调用构造函数AA()。\n"; }AA(const string& name) : m_name(name) { cout << "调用构造函数AA(" << m_name << ")。\n"; }~AA() { cout << "调用了析构函数~AA(" << m_name << ")。\n"; }shared_ptr<BB> m_p;
};
class BB
{
public:string m_name;BB() { cout << m_name << "调用构造函数BB()。\n"; }BB(const string& name) : m_name(name) { cout << "调用构造函数BB(" << m_name << ")。\n"; }~BB() { cout << "调用了析构函数~BB(" << m_name << ")。\n"; }shared_ptr<AA> m_p;
};
int main()
{shared_ptr<AA> pa = make_shared<AA>("西施a");shared_ptr<BB> pb = make_shared<BB>("西施b");pa->m_p = pb;pb->m_p = pa;
}

程序结果:

显然AA和BB都没有析构,这是为什么呢??

AA中有类BB的指针指向pb,BB中有类AA的指针指向pa,所以pa和pb不知道谁先“死”,所以程序没办法判断谁先析构,最后干脆不析构了。

那怎么解决这个问题呢(智能指针的循环引用问题)?这里引出了weak_ptr指针,代码修改如下

基本用法

weak_ptr没有重载 ->和 *操作符,不能直接访问资源。
有以下成员函数:
1)operator=(); // 把shared_ptr或weak_ptr赋值给weak_ptr。
2)expired(); // 判断它指资源是否已过期(已经被销毁)。
3)lock(); // 返回shared_ptr,如果资源已过期,返回空的shared_ptr。
4)reset(); // 将当前weak_ptr指针置为空。
5)swap(); // 交换。
weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。
用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。
提升的行为(lock())是线程安全的。

进行测试

int main()
{shared_ptr<AA> pa = make_shared<AA>("西施a");{shared_ptr<BB> pb = make_shared<BB>("西施b");pa->m_p = pb;pb->m_p = pa;shared_ptr<BB> pp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。if (pp == nullptr)cout << "语句块内部:pa->m_p已过期。\n";elsecout << "语句块内部:pp->m_name=" << pp->m_name << endl;}shared_ptr<BB> pp = pa->m_p.lock();            // 把weak_ptr提升为shared_ptr。if (pp == nullptr)cout << "语句块外部:pa->m_p已过期。\n";elsecout << "语句块外部:pp->m_name=" << pp->m_name << endl;
}

运行结果

删除器

#include <iostream>
#include <memory>
using  namespace std;class AA
{
public:string m_name;AA() { cout << m_name << "调用构造函数AA()。" << endl; }AA(const string& name) : m_name(name) { cout << "调用构造函数AA(" << m_name << ")。" << endl; }~AA() { cout << "调用了析构函数~AA(" << m_name << ")。" << endl; }
};void deletefunc(AA* a) {    // 删除器,普通函数。cout << "自定义删除器(全局函数)。" << endl;delete a;
}struct deleteclass               // 删除器,仿函数。
{void operator()(AA* a) {cout << "自定义删除器(仿函数)。" << endl;delete a;}
};auto deleterlamb = [](AA* a) {   // 删除器,Lambda表达式。cout << "自定义删除器(Lambda)。" << endl;delete a;
};int main()
{shared_ptr<AA> pa1(new AA("张三"));    //不填,默认使用缺省//shared_ptr<AA> pa1(new AA("张三"), deletefunc);//shared_ptr<AA> pa2(new AA("李四"), deleteclass());//shared_ptr<AA> pa3(new AA("王五"), deleterlamb);//unique_ptr<AA,decltype(deletefunc)*> pu1(new AA("张三"), deletefunc);//unique_ptr<AA, void (*)(AA*)> pu0(new AA("李四"), deletefunc);//unique_ptr<AA, deleteclass> pu2(new AA("王五"), deleteclass());//unique_ptr<AA, decltype(deleterlamb)> pu3(new AA("小谢"), deleterlamb);return 0;
}

相关文章:

C++之智能指针

前言普通指针的不足new和new门的内存需要用delete和delete[释放。程序员的主观失误&#xff0c;忘了或漏了释放程序员也不确定何时释放&#xff08;例如多个线程共享同一个对象&#xff0c;没办法确定什么时候释放&#xff09;普通指针的释放类内的指针&#xff0c;在析构函数中…...

Redis实战-session共享之修改登录拦截器

在上一篇中Redis实战之session共享&#xff0c;我们知道了通过Redis实现session共享了&#xff0c;那么token怎么续命呢&#xff1f;怎么刷新用户呢&#xff1f;本来咱们就通过拦截器来实现这两个功能。 登录拦截器优化&#xff1a; 先来看看现在拦截器情况&#xff1a; 拦截…...

数据可视化,流程化处理pycharts-

本文直接进入可视化&#xff0c;输入讲解输入列表生成图片&#xff0c;关于pandas操作看这篇pandas matplotlib 导包后使用 import matplotlib.pyplot as plt饼图 使用 plt.figure 函数设置图片的大小为 15x15 使用 plt.pie 函数绘制饼图&#xff0c;并设置相关的参数&…...

1626_MIT 6.828 lab1课程大纲学习过程整理

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 现在lab1的内容全都学习完了&#xff0c;该做的练习也都做了。接下来&#xff0c;整理一下自己看这一部分课程讲义的一些笔记。 整理之前&#xff0c;先把自己完成…...

12月无情被辞:想给还不会自动化测试的技术人提个醒

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;结果竟没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但是平均水平很让人失望。基本能用一句话概括就是&#xff1a;3年测…...

开发必备技术--docker(使用篇)

文章目录前言Docker的基本概念概念数据卷虚拟网络镜像操作镜像名称镜像命令容器操作基本操作容器创建数据卷操作创建和查看数据卷其他指令实战前言 续接上一篇博文&#xff1a; 开发必备技术–docker&#xff08;一&#xff09; 这也是开学了&#xff0c;假期的最后一篇博文&a…...

2023备战金三银四,Python自动化软件测试面试宝典合集(三)

马上就又到了程序员们躁动不安&#xff0c;蠢蠢欲动的季节~这不&#xff0c;金三银四已然到了家门口&#xff0c;元宵节一过后台就有不少人问我&#xff1a;现在外边大厂面试都问啥想去大厂又怕面试挂面试应该怎么准备测试开发前景如何面试&#xff0c;一个程序员成长之路永恒绕…...

TortoiseGit 使用教程

一、下载工具 这里给大家准备了所有安装包自取 链接&#xff1a;https://pan.baidu.com/s/1xrxxgyNXNQEGD_RjwKnPMg 提取码&#xff1a;qwer 也可在官网自行下载最新版 1.下载git&#xff0c;直接去官网下载Git - Downloads&#xff0c;根据自己的系统合理下载&#xff0c…...

Linux项目自动化构建工具make/Makefile

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 目录 前言 正文 makefile文件与语法规则 make指令与执行逻辑 makefile任务刷新策略 .PHONY修饰为伪目标 最后 前言 会不会写makefile&#xff0c;从一个侧面…...

M100嵌入式自动吞吐式读写器|电动读卡机如何通过C#程序读取社保卡号

M100嵌入式自动吞吐式读写器|电动读卡机是一款双保护门功能读卡器&#xff0c;第一层防尘防异物机械门&#xff0c;第二层电动门。 M100嵌入式自动吞吐式读写器|电动读卡机采用耐高温、耐磨擦、高强度、抗老化的复合型塑胶为主体&#xff0c;在走卡通道两侧镶有不锈钢金属&…...

STM32----搭建Arduino开发环境

搭建Arduino开发环境前言一、Arduino软件1.软件下载2.软件安装3.软件操作二、Cortex官方内核三、烧录下载四、其他第三方内核1.Libmaple内核2.Steve改进的LibMaple 内核3.STMicroelectronics(ST)公司编写的内核总结前言 本章介绍搭建STM32搭建Arduino开发环境&#xff0c;包括…...

华为OD机试 - 事件推送(Python),真机试题

事件推送 题目 同一个数轴 X 上有两个点的集合 A={A1, A2, …, Am} 和 B={B1, B2, …, Bn}, Ai 和 Bj 均为正整数,A、B 已经按照从小到大排好序,A、B 均不为空, 给定一个距离 R (正整数), 列出同时满足如下条件的所有(Ai, Bj)数对: Ai <= BjAi, Bj 之间的距离小于…...

论如何获取CSDN原力

0x00 前言 因为个人需求&#xff0c;所以需要增长原力值&#xff0c;故对原力值的增长方式进行探索和总结。 0x01 正文 参考&#xff1a;https://blog.csdn.net/SoftwareTeacher/article/details/114499372 1.举报了劣质内容&#xff08;博客&#xff0c;下载的资源&#x…...

流程引擎之发展史及对比总结

流程引擎渊源市场上比较有名的开源流程引擎有 jBPM、Activiti、Camunda、Flowable 和 Compileflow。其中 jBPM、Activiti、Flowable、camunda 四个框架同宗同源&#xff0c;祖先都是 jbpm4&#xff0c;开发者只要用过其中一个框架&#xff0c;基本上就会用其它三个。而 Compile…...

【DSView逻辑分析抓取波形CAN步骤-硬件连接-数据解析-底层波形认识CAN-工具使用】

【DSView逻辑分析抓取波形CAN步骤-硬件连接-数据解析-底层波形认识CAN】1、概述2、实验环境3、写在前面的一个问题4、实验准备&#xff08;1&#xff09;硬件连接1&#xff09;CAN卡连接开发板&#xff08;2&#xff09;逻辑分析仪连接开发板&#xff08;2) CAN卡连接软件&…...

C++中的模板

1.模板C 的模板一直是这门语言的一种特殊的艺术&#xff0c;模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理&#xff0c;仅在运行时处理那些最核心的动态服务&#xff0c;进而大幅优化运行期的性能。因此模板也被…...

Maven_第四章 使用Maven:IDEA环境

目录第一节 创建父工程第二节 配置Maven信息第三节 创建Java模块工程第四节 创建Web模块工程1、创建模块2、修改打包方式3、Web 设定4、借助IDEA生成web.xml5、设置 Web 资源的根目录6、测试6.1 创建文件6.2 配置tomcat第五节 其他操作1、在IDEA中执行Maven命令①直接执行②手动…...

RocketMQ基础学习

前言&#xff1a; RocketMQ阿里开源的&#xff0c;一款分布式的消息中间件&#xff0c;它经过阿里的生产环境的高并发、高吞吐的考验&#xff0c;同时&#xff0c;还支持分布式事务等场景。RocketMQ使用Java语言进行开发&#xff0c;方便Java开发者学习源码。但是&#xff0c;R…...

I.MX6ULL内核开发4:设备号的组成与哈希表

目录 一、设备号 二、hash table 一、设备号 文件夹&#xff1a;/home/geralt/linux_driver/kernel/ebf_linux_kernel_6ull_depth1/include/linux/kdev.h 这里面是linux中关于设备号的具体描述 #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1)#defin…...

【博学谷学习记录】大数据课程-学习第六周总结

Hadoop 3.x的版本架构和模型介绍 由于Hadoop 2.0是基于JDK 1.7开发的&#xff0c;而JDK 1.7在2015年4月已停止更新&#xff0c;这直接迫使Hadoop社区基于JDK 1.8重新发布一个新的Hadoop版本&#xff0c;即hadoop 3.0。Hadoop 3.0中引入了一些重要的功能和优化&#xff0c;包括…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...