C++新特性:智能指针

news2024/10/2 8:33:25

一 、为什么需要智能指针

智能指针主要解决以下问题:
1)内存泄漏:内存手动释放,使用智能指针可以自动释放

2)共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题,例如同样的数据帧,但是业务A和业务B处理的逻辑不一样(都是只读)。可以用shared_ptr共享数据帧对象的所有权。线程A释放的时候,shared_ptr的引用计数count - 1,当为0的时候释放数据帧对象指针。
在这里插入图片描述

主要类型:
C++里面的四个智能指针: auto_ptrshared_ptrunique_ptrweak_ptr ,其中后三个是C++11支持,并且第一个已经被C++11弃用。

几个指针的特点:
1)unique_ptr:独占对象的所有权,由于没有引用计数,因此性能较好。
2)shared_ptr:共享对象的所有权,但性能略差。
3)weak_ptr:配合shared_ptr,解决循环引用的问题。

二、shared_ptr 共享的智能指针

2.1 内存模型

shared_ptr 内部包含两个指针,一个指向对象,另一个指向控制块(control block),控制块中包含一个引用计数(reference count), 一个弱计数(weak count)和其它一些数据。
在这里插入图片描述
例如对于

std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;

在这里插入图片描述
std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。再最后一个shared_ptr析构的时候,内存才会被释放。

shared_ptr共享被管理对象,同一时刻可以有多个shared_ptr拥有对象的所有权,当最后一个shared_ptr对象销毁时,被管理对象自动销毁。

2.2 常用函数

s.get()			// 返回shared_ptr中保存的裸指针;
s.reset(...)	// 重置shared_ptr
s.use_count() 	// 返回shared_ptr的强引用计数;
s.unique() 		// use_count()为1,返回true,否则返回false。

reset( )不带参数时,若智能指针s是唯一指向该对象的指针,则释放,并置空。若智能指针P不是唯一指向该对象的指针,则引用计数减少1,同时将P置空。

reset( )带参数时,若智能指针s是唯一指向对象的指针,则释放并指向新的对象。若P不是唯一的指针,则只减少引用计数,并指向新的对象。

2.3 初始化

通过构造函数、reset()make_shared方法来初始化shared_ptr,代码如下:

std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;

// 对于一个未初始化的智能指针,可以通过reset方法来初始化
// 当智能指针有值的时候调用reset会引起引用计数减1
std::shared_ptr<int> p3;
p3.reset(new int(1));
if(p3) {
cout << "p3 is not null";
}

我们应该优先使用make_shared来构造智能指针,因为他更高效。

auto sp1 = make_shared<int>(100);
或
shared_ptr<int> sp1 = make_shared<int>(100);
//相当于
shared_ptr<int> sp1(new shared_ptr<int> sp1(new int(100)););

这是因为为了节省一次内存分配,原来 shared_ptr<Foo> x(new Foo); 需要为 Foo 和 ref_count 各分配一次内存,现在用make_shared()的话,可以一次分配一块足够大的内存,供 Foo 和 ref_count 对象容身。
在这里插入图片描述

但是注意,不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的:

std::shared_ptr<int> p = new int(1);

正确的操作是通过构造函数和辅助方法来初始化

std::shared_ptr<int> p;
p1.reset(new int(1));

例子:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    std::shared_ptr<int> p1;
    p1.reset(new int(1));
    std::shared_ptr<int> p2 = p1;
    // 引用计数此时应该是2
    cout << "p2.use_count() = " << p2.use_count() << endl;
    p1.reset();
    cout << "p1.reset()\n";
    // 引用计数此时应该是2
    cout << "p2.use_count()= " << p2.use_count() << endl;
    if (!p1)
    {
        cout << "p1 is empty\n";
    }
    if (!p2)
    {
        cout << "p2 is empty\n";
    }
    p2.reset();
    cout << "p2.reset()\n";
    cout << "p2.use_count()= " << p2.use_count() << endl;
    if (!p2)
    {
        cout << "p2 is empty\n";
    }
    return 0;
}

结果是

p2.use_count() = 2
p1.reset()
p2.use_count()= 1
p1 is empty
p2.reset()
p2.use_count()= 0
p2 is empty

2.4 获取原始指针get

当需要获取原始指针时,可以通过get方法来返回原始指针,代码如下所示:

std::shared_ptr<int> ptr(new int(1));
int *p = ptr.get();

谨慎使用p.get()的返回值,因为很容易出错且难以排查。

p.get()的返回值就相当于一个裸指针的值,不合适的使用这个值,上述陷阱的所有错误都有可能发生,遵守以下几个约定:

1)不要保存p.get()的返回值 ,无论是保存为裸指针还是shared_ptr都是错误的。
若保存为裸指针不知什么时候就会变成空悬指针,若保存为shared_ptr则产生了独立指针

2)不要delete p.get()的返回值 ,会导致对一块内存delete两次的错误。

2.5 指定删除器

如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器。

#include <iostream>
#include <memory>
using namespace std;
void DeleteIntPtr(int *p) {
  cout << "call DeleteIntPtr" << endl;
  delete p;
}
int main()
{
  std::shared_ptr<int> p(new int(1), DeleteIntPtr);
  return 0;
}

当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存。删除器可以是一个lambda表达式,上面的写法可以改为:

std::shared_ptr<int> p(new int(1), [](int *p) {
    cout << "call lambda delete p" << endl;
    delete p;});

特别的是,当我们用shared_ptr管理动态数组时,需要指定删除器,因为shared_ptr的默认删除器不支持数组对象,代码如下所示:

std::shared_ptr<int> p(new int[10], [](int *p) { delete [] p;});

2.6 使用shared_ptr要注意的问题

1、不要用一个原始指针初始化多个shared_ptr
例如下面错误范例:

int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误

在这里插入图片描述

2、不要在函数实参中创建shared_ptr
对于下面的写法:

function(shared_ptr<int>(new int), g()); //有缺陷

因为C++的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的,一般是从右到左,但也可能从左到右,所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建, 则int内存泄漏了,正确的写法应该是先创建智能指针,代码如下:

shared_ptr<int> p(new int);
function(p, g());

3、通过shared_from_this()返回this指针
不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构,看下面的例子。

#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
  shared_ptr<A> GetSelf()
 {
    return shared_ptr<A>(this); // 不要这么做
 }
 A()
 {
    cout<<"Construction A"<<endl;
 }
  ~A()
 {
    cout << "Destructor A:"<<this << endl;
 }
};
int main()
{
  shared_ptr<A> sp1(new A);
  shared_ptr<A> sp2 = sp1->GetSelf();
  cout<<"sp1.use_count() = "<<sp1.use_count()<<endl;
  cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;
  return 0;
}

运行后调用了两次析构函数。

Construction A
sp1.use_count() = 1
sp2.use_count() = 1
Destructor A:0x1024000
Destructor A:0x1024000

在这个例子中,由于用同一个指针(this) 构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,即指向各自的 control block,但是指向对象都是new int。因此 在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

正确返回this的shared_ptr的做法是:让目标类通过std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this()来返回this的shared_ptr,如下所示。

#include <iostream>
#include <memory>
using namespace std;

class A: public std::enable_shared_from_this<A>
{
public:
  shared_ptr<A>GetSelf()
 {
    return shared_from_this(); //
 }
 A()
 {
    cout<<"Construction A"<<endl;
 }
  ~A()
 {
    cout << "Destructor A" << endl;
 }
};
int main()
{
  shared_ptr<A> sp1(new A);
  shared_ptr<A> sp2 = sp1->GetSelf();  // ok
  cout<<"sp1.use_count() = "<<sp1.use_count()<<endl;
  cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;
  return 0;
}

结果是

Construction A
sp1.use_count() = 2
sp2.use_count() = 2
Destructor A

4、避免循环引用
循环引用会导致内存泄漏

#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
  std::shared_ptr<B> bptr;
  ~A() {
    cout << "A is deleted" << endl;
 }
};
class B {
public:
  std::shared_ptr<A> aptr;
  ~B() {
    cout << "B is deleted" << endl;
 }
};
int main()
{
 {
    std::shared_ptr<A> ap(new A);
    std::shared_ptr<B> bp(new B);
    ap->bptr = bp;
    bp->aptr = ap;
 }
  cout<< "main leave" << endl;  // 循环引用导致ap bp退出了作用域都没有析构
  return 0;
}

循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导致两个指针都不会被析构,产生内存泄漏。

解决的办法是把A和B任何一个成员变量改为weak_ptr,具体方法见weak_ptr章节。

三、unique_ptr独占的智能指针

3.1 unique_ptr是一个独占型的智能指针

unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将
一个unique_ptr赋值给另一个unique_ptr。下面的错误示例。

unique_ptr<T> my_ptr(new T);
unique_ptr<T> my_other_ptr = my_ptr;  // 报错,不能复制

unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过·std::move·来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了。例如

unique_ptr<T> my_ptr(new T); // 正确
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确
unique_ptr<T> ptr = my_ptr;  // 报错,不能复制

std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。

auto upw1(std::make_unique<Widget>());   // with make func
std::unique_ptr<Widget> upw2(new Widget); // without make func

3.2 unique_ptr可以指向一个数组

std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9;
std::shared_ptr<int []> ptr2(new int[10]);  // 这个是不合法的

3.3 unique_ptr需要确定删除器的类型

unique_ptr需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器

std::shared_ptr<int> ptr3(new int(1), [](int *p){delete  p;}); // 正确
std::unique_ptr<int> ptr4(new int(1), [](int *p){delete  p;}); // 错误

关于shared_ptrunique_ptr的使用场景是要根据实际应用需求来选择。

如果希望只有一个智能指针管理资源或者管理数组就用unique_ptr,如果希望多个智能指针管理同一个资源就用shared_ptr

四、weak_ptr弱引用的智能指针

shared_ptr虽然已经很好用了,但是有一点shared_ptr智能指针还是有内存泄露的情况,当两个对象相互
使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptrweak_ptr 只是提供了对管理对象的一个访问手段。

weak_ptr 设计的目的是为配合 shared_ptr而引入的一种智能指针来协助 shared_ptr工作, 它只可以从一个 shared_ptr或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

4.1 基本用法

1)通过use_count()方法获取当前观察资源的引用计数,如下所示:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
cout << wp.use_count() << endl;  //结果讲输出1

2)通过expired()方法判断所观察资源是否已经释放,如下所示

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
if(wp.expired())
  cout << "weak_ptr无效,资源已释放";
else
  cout << "weak_ptr有效";

3)通过lock方法获取监视的shared_ptr

#include <iostream>
#include <memory>
using namespace std;


std::weak_ptr<int> gw;
void f()
{
  auto spt = gw.lock();		
  if(gw.expired()) {
    cout << "gw无效,资源已释放";
 }
  else {
    cout << "gw有效, *spt = " << *spt << endl;
 }
}
int main()
{
 {
 auto sp  = std::make_shared<int>(42);
    gw = sp;
    f();
 }
  f();
return 0;
}

结果是

gw有效, *spt = 42
gw无效,资源已释放

4.2 返回this指针

shared_ptr章节中提到不能直接将this指针返回shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回指针.
原因是std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观察this智能指针。调用shared_from_this()方法,会调用内部这个weak_ptrlock()方法,将所观察的shared_ptr返回

#include <iostream>
#include <memory>
using namespace std;

class A: public std::enable_shared_from_this<A>
{
public:
  shared_ptr<A>GetSelf()
 {
    return shared_from_this(); //
 }
 A()
 {
    cout<<"Construction A"<<endl;
 }
  ~A()
 {
    cout << "Destructor A" << endl;
 }
};
int main()
{
  shared_ptr<A> sp1(new A);
  shared_ptr<A> sp2 = sp1->GetSelf();  // ok
  cout<<"sp1.use_count() = "<<sp1.use_count()<<endl;
  cout<<"sp2.use_count() = "<<sp2.use_count()<<endl;
  return 0;
}

结果是

Construction A
sp1.use_count() = 2
sp2.use_count() = 2
Destructor A

在外面创建A对象的智能指针和通过对象返回this的智能指针都是安全的,因为shared_from_this()是内部的weak_ptr调用lock()方法之后返回的智能指针,在离开作用域之后,sp1的引用计数减为0,A对象会被析构,不会出现A对象被析构两次的问题。

需要注意的是,获取自身智能指针的函数尽在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

需要注意的是,获取自身智能指针的函数尽在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

4.3 解决循环引用问题

shared_ptr章节提到智能指针循环引用的问题,因为智能指针的循环引用会导致内存泄漏,可以通过weak_ptr解决该问题,只要将A或B的任意一个成员变量改为weak_ptr

#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
  std::weak_ptr<B> bptr;
  ~A() {
    cout << "A is deleted" << endl;
 }
};
class B {
public:
  std::shared_ptr<A> aptr;
  ~B() {
    cout << "B is deleted" << endl;
 }
};
int main()
{
 {
    std::shared_ptr<A> ap(new A);
    cout<< "ap.use_count():"<< ap.use_count()<<endl;
    std::shared_ptr<B> bp(new B);
    cout<< "bp.use_count():"<<bp.use_count()<<endl;

    cout<<"===================="<<endl;
    ap->bptr = bp;
    cout<< "ap.use_count():"<<ap.use_count()<<endl;
    cout<< "bp.use_count():"<<bp.use_count()<<endl;
    
    cout<<"===================="<<endl;
    bp->aptr = ap;
    cout<< "ap.use_count():"<<ap.use_count()<<endl;
    cout<< "bp.use_count():"<<bp.use_count()<<endl;
 }
  cout<< "main leave" << endl;  // 循环引用导致ap bp退出了作用域都没有析构
  return 0;
}
ap.use_count():1
bp.use_count():1
====================
ap.use_count():1
bp.use_count():1
====================
ap.use_count():2
bp.use_count():1
B is deleted
A is deleted
main leave

这样在对B的成员赋值时,即执行ap->bptr = bp;;时,由于bptrweak_ptr,它并不会增加引用计数,所以bp的引用计数仍然会是1。
在离开作用域之后,bp的引用计数为减为0,B指针会被析构,析构后其内部的aptr的引用计数会被减为1,然后在离开作用域后ap引用计数又从1减为0,A对象也被析构,不会发生内存泄漏.

4.4 使用注意事项

1、weak_ptr在使用前需要检查合法性。

weak_ptr<int> wp;
{
  shared_ptr<int>  sp(new int(1));  //sp.use_count()==1
  wp = sp; //wp不会改变引用计数,所以sp.use_count()==1
  shared_ptr<int> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
shared_ptr<int> sp_null = wp.lock(); //sp_null .use_count()==0;

因为上述代码中spsp_ok离开了作用域,其容纳的对象已经被释放了,得到了一个容纳NULL指针的sp_null对象。

在使用wp前需要调用wp.expired()函数判断一下。因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法指导自己所容纳的那个指针资源的当前状态。

weak_ptr<int> wp;
shared_ptr<int> sp_ok;
{
  shared_ptr<int>  sp(new int(1));  //sp.use_count()==1
  wp = sp; //wp不会改变引用计数,所以sp.use_count()==1
  sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
// 在这个作用域结束后,sp 和其所管理的内存将会被销毁,因为 sp 的引用计数变为 0,触发内存释放。
if(wp.expired()) {
  cout << "shared_ptr is destroy" << endl;
} else {
  cout << "shared_ptr no destroy" << endl;
}
shared_ptr no destroy

在作用域内,创建了一个新的shared_ptr对象sp并将其所有权转交给wp,然后通过调用wp.lock()方法从wp中获取了shared_ptr的所有权。由于wp仍然有效,所以输出结果为"shared_ptr no destroy"。当作用域结束时,sp所管理的int对象会被销毁,但这并不影响wp的有效性。

五、为什么多线程读写 shared_ptr 要加锁?

为什么多线程读写 shared_ptr 要加锁?

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接,详细查看详细的服务器课程

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

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

相关文章

台灯应该买什么样的才能护眼?推荐五款安全健康的护眼台灯

台灯主要还是以护眼台灯为主&#xff0c;因为不仅色谱丰富&#xff0c;贴近自然色的全光谱色彩&#xff0c;通常显色指数都能达到Ra95以上&#xff0c;显色能力特别强&#xff0c;而且还具有其他防辐射危害、提高光线舒适度的特性&#xff0c;比如侧发光技术、漫反射技术等大大…

社科院杜兰金融硕士不出国门获国家顶尖金融管理硕士学位及教育部留服中心学历学位认证

社科院杜兰金融硕士不出国门获国家顶尖金融管理硕士学位及教育部留服中心学历学位认证 中国社科院与美国杜兰大学金融管理硕士从默默无闻到人尽皆知&#xff0c;已在中外合作办学硕士领域走出了一条难以被模仿、无法被替代的特质。中国社会科学院与杜兰大学充分代表了中美两国…

JavaSE(一)--Java语法

&#xff01;&#xff01;&#xff01;本文适合有c和c基础的&#xff0c;通过比较c和java的不同&#xff0c;快速入门java。&#xff01;&#xff01;&#xff01; 这篇文章主要是介绍和c不同的地方&#xff0c;没说的知识点按照c写就行了。 零.整体的结构 Java是面向对象的程…

Python 套接字编程完整指南

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 连接设备以交换信息是网络的全部意义所在。套接字是有效网络通信的重要组成部分&#xff0c;因为它们是用于通过本地或全球网络以及同一台计算机上的不同进程在设备之间传输消息的基本概念。它们提供了一个低级接口&am…

Java程序员学vue3最好的方式就是搭建后台管理模板

前言 作为Java程序员&#xff0c;vue3还是有必要学的&#xff0c;毕竟是国内最受欢迎的前端JS框架&#xff0c;你现在接手的项目&#xff0c;前端部分几乎都会和vue沾边&#xff0c;尤其是中小企业。 vue3作为新的大版本&#xff0c;相较于vue2改动还是很多的&#xff0c;目前企…

Kali Linux渗透测试技术介绍【文末送书】

文章目录 写在前面一、什么是Kali Linux二、渗透测试基础概述和方法论三、好书推荐1. 书籍简介2. 读者对象3. 随书资源 写作末尾 写在前面 对于企业网络安全建设工作的质量保障&#xff0c;业界普遍遵循PDCA&#xff08;计划&#xff08;Plan&#xff09;、实施&#xff08;Do…

基于Python开发的玛丽大冒险小游戏(源码+可执行程序exe文件+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python开发的玛丽冒险小游戏程序&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xf…

【PCL-11】提取平面上层的目标物,剔除平面下层目标物

因项目需求,需提取平面上的物体,不提取平面下的物体,尝试采用超体聚类+LCCP分割的方式,但由于上层点云模型一侧有空洞,导致分割效果不理想。 这里采用pcl::ExtractPolygonalPrismData类,实现平面上物体的提取。 pcl::ExtractPolygonalPrismData类是通过设定处于同一平面模…

对于html和css初学者,你有什么值得推荐的简单的网页适合练习?

前言 html和css方面个人认为主要是在于css的学习&#xff0c;变化最多的也在于css&#xff0c;下面主要是介绍一些css相关的练习网站及一些项目&#xff0c;希望对你有帮助~ 网站推荐 1、CODEPEN 代码与所展示的页面相互对应&#xff0c;你可以在上面找到其他人已经写好的de…

胖小酱之不龟手之药寓言故事

宋国有个人善于炼制一种预防皮肤冻裂的药膏。在冬天如果把这种药膏涂在手上的话就能够防止手冻裂&#xff0c;所以他家祖祖辈辈就靠冬天的时候&#xff0c;在河边把这些药膏卖给洗衣服的人们来为生。 有个外地人听说他们家善于炼制这种药膏&#xff0c;便寻上门来&#xff0c;情…

同旺科技USB to I2C 适配器烧写 Arduino 模块

所需设备&#xff1a; 内附链接 1、同旺科技USB to I2C 适配器 2、Arduino 模块 硬件连接&#xff1a; 用同旺科技USB to I2C 适配器连接芯片的TX、RX、GND; 打开Arduino IDE编辑工具&#xff0c; 点击“上传”按钮&#xff0c;完成程序的编译和烧录&#xff1b;

RabbtiMQ的安装与在Springboot中的使用!!!

一、安装Erlang与Rabbitmq 安装教程本教程是在centos8下试验的&#xff0c;其实linux系统的都差不多RabbitMQ官方&#xff1a;Messaging that just works — RabbitMQRabbitMQ是开源AMQP实现&#xff0c;服务器端用Erlang语言编写&#xff0c;Python、Ruby、 NET、Java、JMS、c…

使用Python操作MySQL数据库

准备 安装Python,打开命令提示符&#xff0c;我已经安装成功了 安装Mysql我也安装成功了 我在用户的86188下利用记事本写了一个.py的python代码&#xff0c;在命令提示符中运行 Python自带的集成式开发环境,在电脑搜索框直接IDEA你也会打开 一&#xff0c;建立连接 使用Python…

AR眼镜: 与人并行的智能伙伴

AR眼镜&#xff1a; 与人并行的智能伙伴 增强现实&#xff08;Augmented Reality&#xff0c;AR&#xff09;眼镜是一种将虚拟信息与真实世界进行融合的设备&#xff0c;通过眼镜或头戴设备让用户能够看到真实世界&#xff0c;并在其上叠加数字内容和图像。目前工业级AR眼镜已…

python考研志愿填报模拟系统vue

本系统提供给管理员对学生、院校、研究生信息、专业信息、学院信息等诸多功能进行管理。本系统对于学生输入的任何信息都进行了一定的验证&#xff0c;为管理员操作提高了效率&#xff0c;也使其数据安全性得到了保障。本考研志愿填报模拟系统以Django作为框架&#xff0c;B/S模…

2022蓝帽杯半决赛

2022蓝帽杯取证题目解压密码附件 链接&#xff1a;https://pan.baidu.com/s/1AS0wVdjZxt46zaDcDzxdaQ 提取码&#xff1a;scpc –来自百度网盘超级会员V4的分享 解压密码7(G?fu9A8sdgfMsfsdrfE4q6#cf7af0fc1c 参考奇安信攻防社区-第六届“蓝帽杯”半决赛取证题目官方解析 20…

【项目测试报告】博客系统 + 在线聊天室

文章目录 一、项目介绍1.1 核心技术1.2 核心功能1.3 技术亮点1.4 前端页面设计登录页面注册页面博客广场页面搜索结果页面个人博客页面个人中心页面我的关注/粉丝页面聊天室页面写博客页面我的草稿修改博客页面博客详情页他人博客主页 二、功能测试2.1 登录测试用例2.2 注册测试…

OpenAI科学家Jason Wei专访:思维链灵感来源于冥想丨智源独家

导读 在大模型的研发道路上&#xff0c;思维链、指令微调和智能涌现等关键思想备受关注。正是思维链技术&#xff08;Chain of Thought&#xff09;让大模型能够涌现出一系列神奇的能力&#xff0c;成为了现代大语言模型产生「涌现」的底层技术。思维链旨在通过向大语言模型展示…

百度飞浆OCR识别表格入门python实践

1. 百度飞桨&#xff08;PaddlePaddle&#xff09; 百度飞桨&#xff08;PaddlePaddle&#xff09;是百度推出的一款深度学习平台&#xff0c;旨在为开发者提供强大的深度学习框架和工具。飞桨提供了包括OCR&#xff08;光学字符识别&#xff09;在内的多种功能&#xff0c;可…

【C++】内联函数 ① ( 内联函数引入 | 内联函数语法 )

文章目录 一、内联函数引入1、内联函数引入2、代码示例 - 宏代码片段 与 内联函数 二、内联函数语法1、内联函数语法说明2、代码示例 - 内联函数基本语法 一、内联函数引入 1、内联函数引入 " 内联函数 " 是 C 语言中的一种特殊函数 , 其目的是为了提高程序的执行效率…