【C++进阶之路】C++11(中)

一、可变参数模板

1.基本概念

 想要了解C语言的可变参数列表的原理可见:可变参数列表

 这个跟C语言的可变参数列表有一定的关系,常用的printf与scanf的参数就包含可变参数列表。

 那么可变参数模板是什么呢?举个例子便一目了然。

template<class...Arg>
void ShowList(Arg... arg)
{//...
}
int main()
{ShowList();ShowList(1, 2, 3);ShowList('a', 'b', 'c');return 0;
}

 这就是可变参数模板,可以传多个参数,这里的Arg称为模板的可变参数包,这里的arg称为形参的可变参数包(包括0-N个参数)。

但是这里有一个问题即如何用这个参数包呢?

  1. 递归展开
void _ShowList()
{cout<< endl;
}template<class T,class...Arg>
void _ShowList(T val, Arg... arg)
{cout << val << " ";_ShowList(arg...);
}template<class...Arg>
void ShowList( Arg... arg)
{_ShowList(arg...);
}
int main()
{ShowList();ShowList(1, 2, 3);ShowList('a', 'b', 'c');return 0;
}
  • 编译器以类似递归的方式(调用的不是同一个函数,是来自一个模板实例化出的函数)自动推导出参数包的类型然后进行打印。

运行结果:
在这里插入图片描述

  1. 逗号表达式
template<class T>
int PrintArg(T val)
{cout << val << " ";return 0;
}void ShowList()
{cout << endl;
}template<class... Arg>
void ShowList(Arg... arg)
{int arr[] = { PrintArg(arg)... };//处理成类似与int arr[] = { PrintArg(arg), PrintArg(arg),PrintArg(arg)};这样的形式。//这是编译链接阶段处理的结果。cout << endl;
}int main()
{ShowList();ShowList(1, 2, 3);ShowList('a', 'b', 'c');return 0;
}

运行结果:
在这里插入图片描述

2.应用场景

1.提升传参的灵活性

例:

class Date
{
public:Date(int year = 0, int month = 0, int day = 0):_year(year),_month(month),_day(day){}
public:int _year;int _month;int _day;
};template<class ...Args>
Date* CreatDate(Args... args)
{Date* ret = new Date(args...);return ret;
}int main()
{Date* date1 = CreatDate(1);Date* date2 = CreatDate(1,2);Date* date3 = CreatDate(1,2,3);Date* date4 = CreatDate(Date(1,2,3));return 0;
}
  • 不仅可以调用构造函数,也可调用默认的拷贝构造函数,进而提高了灵活性。

2.接口——emplace_back

参数:
在这里插入图片描述

  • 说明:这里在可变参数模板的基础上添加了万能引用。
int main()
{list<pair<int, MY_STL::string>>lt;//先调用pair的构造函数再调用右值的移动拷贝构造。lt.push_back(make_pair(1, "张三"));cout << endl;lt.push_back({ 2, "李四" });cout << endl << endl;//作为参数包(引用)传递下去,直接调用list的构造函数进行构造lt.emplace_back(3, "王五");cout << endl;lt.emplace_back(make_pair(4, "赵六"));//这里编译器进行了优化,也是直接调用构造函数进行初始化。return 0;
}
  • 可见push_back虽然多调用了两次构造但好在是移动构造消耗不大,emplace_back直接将参数包传下去,直接调用构造函数进行初始化,总而言之与push_back有区别但性能上相差不大。

  • 举例 : list的emplace_back

//list里面的实现接口template <class... Args>void push_back(Args&&... args){insert(end(), args...);}template <class... Args>void emplace_back(Args&&... args){push_back(args...);}template <class... Args>void insert(iterator pos, Args&&... args){Node* newnode = new Node(args...);Node* cur = pos._node;Node* prev = cur->_prev;newnode->_next = cur;newnode->_prev = prev;cur->_prev = newnode;prev->_next = newnode;_size++;}
//Node的实现接口template<class ...Args>list_node(Args&& ...arg):_val(arg...), _next(nullptr), _prev(nullptr){}
//实现逻辑——不断传参数包,将参数包传给构造函数进行初始化。

二、包装器

1. 简单实现

 如何用外界控制某段执行逻辑?

  • C语言阶段我们可以通过传函数指针
  • C++阶段我们可以通过传仿函数或者lambda(底层是仿函数)

 这三者有没有什么共同的特性呢?就是可以像函数一样进行使用。如果实现的功能一样那么参数和返回值是一样的。

 根据这两个性质,我们可以将这三者抽象成一个模板,以实现减法举例:

template<class T1,class T2,class T3>
double fun(T1 f, const T2& x, const T3& y)
{return f(x, y);
}
double sub(int x, int y)
{return x - y;
}
struct Sub
{double operator()(int x, int y){return x - y;}
};
int main()
{auto x = [](int x, int y)->double {return x - y;};//lambda表达式。cout << fun(x, 1, 2) << endl;cout << fun(sub, 4, 2) << endl;Sub s;cout << fun(s, 9, 2) << endl;return 0;
}

 这样的一个模板就将这三者进行泛化,都可以通过模板实例化使用,也就是实现了包装的作用,当然这只是最简单的用法,下面我们就进入库里面的包装器的使用。

2. 基本语法与使用

在这里插入图片描述

重点:class function<Ret()Args...>;
1. Ret 返回值
2. Args... 函数参数

简单使用:

double sub(int x, int y)
{return x - y;
}
struct Sub
{double operator()(int x, int y){return x - y;}
};
int main()
{auto x = [](int x, int y)->double {return x - y;};function<double(int, int)> f1 = x;function<double(int, int)> f2 = s;function<double(int, int)> f3 = sub;cout << f1(2 , 3) << endl;cout << f2(2, 9) << endl;cout << f3(9, 9) << endl;return 0;
}

更为实际的使用:
比如:逆波兰表达式的求值

class Solution {
public:int evalRPN(vector<string>& tokens) {map<string,function<int(int,int)>> s = {{"+",[](int x,int y){return x + y;}},{"-",[](int x,int y){return x - y;}},{"*",[](int x,int y){return x * y;}},{"/",[](int x,int y){return x / y;}}};stack<int> st;int size = tokens.size();for(int i = 0; i < size; i++){if(tokens[i] == "+" || tokens[i] == "-"|| tokens[i] == "*" || tokens[i] == "/"){int right = st.top();st.pop();int left = st.top();st.pop(); st.push(s[tokens[i]](left,right));}else{st.push(stoi(tokens[i]));}}return st.top();}
};

 仔细品味这段代码,就会发现包装器的好用之处,其原因在于直接将一类功能类似的函数进行泛化,可以通过具体的反应进行直接调用,还有我们所写的一类代码统一的出现在一个地方这样便于维护

三、bind

 bind也是函数的封装,类似与包装器,不过更详细的是对参数的个数和传参顺序的封装,下面我们来看看如何具体使用。

1.基本语法

在这里插入图片描述

  • 将参数1(函数指针/ 仿函数/lambda),剩余函数参数传进去即可,具体要进行分类进讨论。

2.常见用法

1.函数指针

  • 改变参数顺序
double sub(int x, int y)
{return x - y;
}
int main()
{function<double(int, int)> f1 = bind(sub, placeholders::_1,\placeholders::_2);cout << f1(1, 2) << endl;function<double(int, int)> f2 = bind(sub, placeholders::_2,\placeholders::_1);cout << f2(1, 2) << endl;return 0;
}

逻辑图解:
在这里插入图片描述

  • 改变参数个数
double sub1(int x, double rate, int y)
{return (x - y) * rate;
}
int main()
{function<double(int, int)> ff1 = bind(sub1, placeholders::_1,\1.5, placeholders::_2);//在定义期间固定参数,类似与缺省值的效果。cout << ff1(2, 1) << endl;auto ff2 = bind(sub1, placeholders::_1, 1, placeholders::_2);cout << ff2(3, 1) << endl;
}

逻辑图解:
在这里插入图片描述

  • 说明:lambda与函数指针的用法相同。

2.仿函数

struct SubType
{static double sub(int x, int y)//没有this指针,不用进行传参{return x - y;}double ssub(int x, int y){return x - y;}
};
int main()
{function<double(int, int)> pf1 = bind(SubType::sub,\placeholders::_1, placeholders::_2);//sub为static函数,没有this指针,只需指定作用域即可。SubType st;		//语法规定普通成员函数传参需加&auto pf3 = bind(&SubType::ssub, &st, placeholders::_1,\placeholders::_2);//ssub为普通成员函数,有this指针,因此需要指定作用域外加传this指针。auto pf2 = bind(&SubType::ssub, SubType(), placeholders::_1,\placeholders::_2);//也可用对象进行调用。return 0;
}

 总之,bind可以改变参数的个数和顺序,改变参数的个数可以固定参数值和减少传参改变参数顺序,可以更加符合函数使用者的习惯(比如在strcpy一般第一个参数是dest,第二个参数为source,如果实现接口的程序员将参数写反了,我们可以通过包装更加符合我们的代码编写习惯。) 。

四、智能指针

 为了解决异常的执行流乱跳而导致内存泄漏的问题:

void func()
{//假设new int 可能会抛出bad_alloc的异常。int* ptr1 = new int(1);//当运行到此处ptr1出错时,我们只需抛出异常即可int* ptr2 = new int(2);//当运行到此处出ptr2错时,我们需要释放ptr1,然后抛出异常。int* ptr3 = new int(3);//当运行到此处ptr3出错时,我们需要释放ptr2,然后向外层抛出异常。delete ptr1;delete ptr2;delete ptr3;
}

 第一种方式我们可以通过try catch进行解决:

void func1()
{//假设new int 也可能会抛出异常。//根据异常的捕获与处理,我们可以这样解决。try{int* ptr1 = new int(1);//...try{int* ptr2 = new int(2);//...try{int* ptr3 = new int(3);//...delete ptr3;}catch (...){//当运行到此处ptr3出错时,我们需要释放ptr2,然后向外层抛出异常。delete ptr2;throw;}delete ptr2;}catch (...){//当运行到此处出ptr2错时,我们需要释放ptr1,然后抛出异常。delete ptr1;throw;}delete ptr1;}catch (...){//当运行到此处ptr1出错时,我们只需抛出异常即可throw;}
}
int main()
{try{func1();}catch (const exception& e){cout << e.what() << endl;}catch (...){//...未知异常;}return 0;
}
  • 很明显,每开辟一次我们需要进行try catch进行手动对异常进行捕获,很麻烦,那有没有简单的方式呢?很明显是有的。

  • RAII思想,资源获取即初始化,也就是用局部对象对资源进行管理,出作用域时会自动调用析构函数对资源进行释放。

就像这样:

template<class T>
class smart_ptr
{
public:smart_ptr(T* ptr):_ptr(ptr){}~smart_ptr(){if (_ptr)delete _ptr;}
private:T* _ptr;
};
void func()
{//假设new int 可能会抛出bad_alloc的异常。smart_ptr<int> ptr1 (new int(1));//当运行到此处ptr1出错时,我们只需抛出异常即可smart_ptr<int> ptr2(new int(2));//当运行到此处ptr2出错时,出作用域自动释放ptr1,然后抛出异常。smart_ptr<int> ptr3(new int(3));//当运行到此处ptr3出错时,出作用域自动释放ptr1,ptr2,然后向外层抛出异常。
}

 这样就简单的使用了RAII思想解决问题,但是如果是赋值问题与拷贝该如何解决呢?在使用默认的成员函数的情况下,可能会出现内存泄漏和重复释放的风险。下面就让我们看看库里面是如何实现的吧!

1.auto_ptr

1.1基本使用

class A
{
public:A(int a):_a(a){}~A(){cout << "~A()" << endl;}
private:int _a = 0;
};
int main()
{auto_ptr<A> ap1(new A(1));auto_ptr<A> ap2(new A(2));auto_ptr<A> ap3(ap1);//资源管理权的转移。ap1 = ap2;//调用析构函数先释放ap1的资源,再将ap2的资源转移给ap1。return 0;
}

监视逻辑:从ap3的初始化开始。

在这里插入图片描述

  • 赋值与拷贝的实现方式是对资源的管理权进行转移。

1.2基本实现

namespace MY_SMART_PTR
{template<class T>class auto_ptr{public:auto_ptr(T* ptr = nullptr):_ptr(ptr){}~auto_ptr(){if (_ptr)delete _ptr;}//既然是指针就得像指针一样进行访问。T& operator *(){return *_ptr;}T* operator ->(){return _ptr;}auto_ptr( auto_ptr<T>& ptr):_ptr(ptr._ptr){ptr._ptr = nullptr;}auto_ptr<T>& operator = (auto_ptr& ptr){if(_ptr)delete _ptr;_ptr = ptr._ptr;ptr._ptr = nullptr;return *this;}private:T* _ptr;};
}

2.unique_ptr

1.1基本使用

  • 对资源的使用权进行转移。
class A
{
public:A(int a):_a(a){}~A(){cout << "~A()" << endl;}
private:int _a = 0;
};
int main()
{unique_ptr<A> up1(new A(1));unique_ptr<A> up2(new A(2));unique_ptr<A> up3(new A(3));up1 = up2;//直接禁掉了。unique_ptr<A> up3(up1);//也是被删除了return 0;
}

会直接进行报错:
在这里插入图片描述

1.2基本实现

  • 不可进行共享资源
namespace MY_SMART_PTR
{template<class T>class unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr)delete _ptr;}T& operator *(){return *_ptr;}T* operator ->(){return _ptr;}//两种方式://1.C++98之前构造函数私有+声明进行实现。//2.C++11沿用之前的default与纯虚函数 == 0的写法,设为delete进行删除。unique_ptr(const unique_ptr<T>& ptr) = delete;unique_ptr<T>& operator = (unique_ptr<T>& ptr) = delete;private:T* _ptr;};
}

3.shared_ptr

1.1基本使用

  • 可以进行共享资源。
class A
{
public:A(int a):_a(a){}~A(){cout << "~A()" << endl;}
private:int _a = 0;
};
int main()
{shared_ptr<A> sp1(new A(1));shared_ptr<A> sp2(new A(2));shared_ptr<A> sp3 = sp1;//采取引用计数sp3与sp1共享资源,析构采取引用计数。sp1 = sp2;//实现需注意与自身的赋值return 0;
}

实现图解:
在这里插入图片描述

  • 当对象进行拷贝构造时,将对象进行浅拷贝,并进行引用计数。
  • 当对象进行赋值时,对被赋值的对象的引用计数进行检查,对引用计数减减,如果引用计数为0。则进行资源的清理。

1.2基本实现

  • 开辟一个count的资源对指向资源的对象进行计数。
namespace MY_SMART_PTR
{template<class T>class shared_ptr{public:shared_ptr(T* ptr ):_ptr(ptr),_pcount(new int(1)){}~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}T& operator *(){return *_ptr;}T* operator ->(){return _ptr;}shared_ptr(shared_ptr<T>& ptr):_ptr(ptr._ptr),_pcount(ptr._pcount){if(_pcount)(*_pcount)++;}shared_ptr<T>& operator = (shared_ptr<T>& ptr){//判断是否相等,包括资源指向空的情况if (ptr._pcount == _pcount)return *this;//*this的_pcount的内容进行减减,判断是否为0if (_pcount && --(*_pcount) == 0){delete _ptr;delete _pcount;}//对ptr指向的pcount加加_ptr = ptr._ptr;_pcount = ptr._pcount;if(_pcount)++(*_pcount);return *this;}T* get()const{return _ptr;}private:T* _ptr;int* _pcount;};
}

shared_ptr看起来是完美的,那么有没有什么缺陷呢?

思考下面一段代码:

struct Node
{
public:~Node(){cout << "~Node()" << endl;}shared_ptr<Node> _prev;shared_ptr<Node> _next;
};
int main()
{//循环引用——关键与难点//画图分析shared_ptr<Node> sp1(new Node);shared_ptr<Node> sp2(new Node);sp1->_next = sp2;sp2->_prev = sp1;return 0;
}
  • 运行分析程序的结果:

在这里插入图片描述
 运行结果看似正确,其实隐含了内存泄漏,因为sp1和sp2的资源没有被释放,这是为什么呢?

我们画图进行分析:
在这里插入图片描述

  • 关键在于对象析构时sp1和sp2分别只调用了一次析构函数,使引用计数减为1,最后对资源没有进行释放,那么如何解决这个问题呢?就引出了weak_ptr。
  • 这种现象我们称之为循环引用。

4.weak_ptr

1.1基本使用

{
public:~Node(){cout << "~Node()" << endl;}weak_ptr<Node> _prev;weak_ptr<Node> _next;
};
int main()
{//循环引用——关键与难点//画图分析shared_ptr<Node> sp1(new Node);shared_ptr<Node> sp2(new Node);sp1->_next = sp2;sp2->_prev = sp1;return 0;
}
  • 其实现原理也很简单,就是weak_ptr中没有引用计数,且不参与资源的析构。

图解:
在这里插入图片描述

  • 在析构时调用两次即可对资源进行释放。

1.2基本实现

  • 实现shared_ptr转weak_ptr的赋值与拷贝构造。
  • 没有引用计数且不需要写析构函数。
namespace MY_SMART_PTR
{template<class T>class weak_ptr{public:weak_ptr(){}T& operator *(){return *_ptr;}T* operator ->(){return _ptr;}weak_ptr(shared_ptr<T>& ptr):_ptr(ptr.get()){}weak_ptr<T>& operator = (shared_ptr<T>& ptr){_ptr = ptr.get();return *this;}private:T* _ptr = nullptr;};
}
  • 1.weak_ptr不是RAII的智能指针,是专门用来解决循环引用的问题。
    2.weak_ptr不增加引用计数,只参与资源使用,不参与资源的释放。

总结

 今天的分享就到这里了,如果有所帮助,不妨点个赞鼓励一下,我们下篇文章再见~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双周赛114(模拟、枚举 + 哈希、DFS)

文章目录 双周赛114[2869. 收集元素的最少操作次数](https://leetcode.cn/problems/minimum-operations-to-collect-elements/)模拟 [2870. 使数组为空的最少操作次数](https://leetcode.cn/problems/minimum-number-of-operations-to-make-array-empty/)哈希 枚举 [2871. 将数…

点击劫持:X-Frame-Options 未配置

前言 X-Frame-Options作为HTTP头的一部分&#xff0c;是一种用于保护网站免受点击劫持攻击的安全措施。网站可以通过设置X-Frame-Options或csp报头来控制网站本身是否可以被嵌套到iframe中。 漏洞描述 Clickjacking&#xff08;点击劫持&#xff09;是一种安全漏洞&#xff…

[天翼杯 2021]esay_eval - RCE(disabled_function绕过||AS_Redis绕过)+反序列化(大小写wakeup绕过)

[天翼杯 2021]esay_eval 1 解题流程1.1 分析1.2 解题1.2.1 一阶段1.2.2 二阶段二、思考总结题目代码: <?php class A{public $code = "";

湖州OLED透明拼接屏技术应用引领现代化旅游观光方式

湖州市位于中国浙江省北部&#xff0c;拥有悠久的历史和丰富的文化遗产。湖州市以其美丽的湖泊和秀丽的自然风光而闻名。 作为中国重要的历史文化名城之一&#xff0c;湖州市有着丰富的文化遗产和历史资源&#xff0c;如古城墙、古建筑和古镇等。 这为OLED透明拼接屏技术的应用…

canvas力导布局

老规矩&#xff0c;先上效果图 <html><head><style>* {margin: 0;padding: 0;}canvas {display: block;width: 100%;height: 100%;background: #000;}</style> </head><body><canvas id"network"></canvas> </…

如何避免 IDEA 每次重启都index

如何避免 IDEA 每次重启都index 在 IntelliJ IDEA 中&#xff0c;可以通过以下几个步骤来避免每次重启时索引&#xff1a; 打开 File -> Settings 菜单。在左侧的菜单栏中选择 “Appearance & Behavior” -> “System Settings” -> “Synchronization”。 在右…

力扣第501题 二叉树的众数 c++ (暴力 加 双指针优化)

题目 501. 二叉搜索树中的众数 简单 相关标签 树 深度优先搜索 二叉搜索树 二叉树 给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 …

【Redis】Set集合相关的命令

目录 命令SADDSMEMBERSSISMEMBERSCARDSPOPSMOVESREMSINTERSINTERSTORESUNIONSUNIONSTORESDIFFSDIFFSTORE 命令 SADD 将⼀个或者多个元素添加到set中。注意&#xff0c;重复的元素⽆法添加到set中。 SADD key member [member ...]SMEMBERS 获取⼀个set中的所有元素&#xff0…

js事件循环详解

事件循环简介 JavaScript的事件循环是一种处理异步事件和回调函数的机制&#xff0c;它是在浏览器或Node.js环境中运行&#xff0c;用于管理任务队列和调用栈&#xff0c;以及在适当的时候执行回调函数。 事件循环的基本原理是&#xff0c;JavaScript引擎在空闲时等待事件的到…

苹果ios开发者ipa文件包内测人数签名真机数量满了应该怎么做?

苹果ios开发者ipa文件包内测人数签名真机数量满了应该怎么做&#xff1f; 有人总是问我开发者的设备满了怎么做才可以让设备增加&#xff1f;或者我要怎么做才能让员工的设备都可以安装&#xff0c;那么首先我们要做到的就是要知道我们的开发者都是拥有多少内测设备&#xff1f…

jupyter notebook如何实现代码提示功能?

jupyter notebook在数据分析中使用非常方便&#xff0c;但是没有代码提示功能&#xff0c;让人感觉有一点点遗憾&#xff1f;如何实现代码提示功能呢&#xff1f;以下实现亲测有效。 本人python版本是3.8. 首先关闭jupyter notebook&#xff0c;安装相关的库。 一、需要提前…

MongoDB——centOS7环境Mongodb权限管理(图解版)

目录 一、MongDB权限概述1.1、MongDB权限概述1.2、MongDB权限列表 二、Mongodb权限管理示例2.1、创建账号2.1.1、创建管理员用户2.1.2、开启认证2.1.3、创建普通账号 一、MongDB权限概述 1.1、MongDB权限概述 mongodb是没有默认管理员账号&#xff0c;所以要先添加管理员账号…

【Python 零基础入门】 函数

【Python 零基础入门】第五课 函数 【Python 零基础入门】第五课 函数函数在生活中的类比函数为什么要使用函数函数的格式无参函数含参函数 参数形参实参 变量作用域局部变量全局变量 递归函数基本的递归斐波那契数列 Lambda 表达式高阶函数map 函数filter 函数reduce 函数结合…

Node历史版本下载及配置npm镜像

https://nodejs.org/en/download/releases 点击对应版本Release,选择合适的包&#xff0c;进行下载安装。 配置国内镜像 npm config set registry https://registry.npmmirror.com/

Practical Memory Leak Detection using Guarded Value-Flow Analysis 论文阅读

本文于 2007 年投稿于 ACM-SIGPLAN 会议1。 概述 指针在代码编写过程中可能出现以下两种问题&#xff1a; 存在一条执行路径&#xff0c;指针未成功释放&#xff08;内存泄漏&#xff09;&#xff0c;如下面代码中注释部分所表明的&#xff1a; int foo() {int *p malloc(4 …

上海亚商投顾:沪指冲高回落 医药、芯片股全天领涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日小幅反弹&#xff0c;创业板指盘中涨超1.6%&#xff0c;午后涨幅有所收窄。医药医疗股全线走强&#…

LLM - 旋转位置编码 RoPE 代码详解

目录 一.引言 二.RoPE 理论 1.RoPE 矩阵形式 2.RoPE 图例形式 3.RoPE 实践分析 三.RoPE 代码分析 1.源码获取 2.源码分析 3.rotary_emb 3.1 __init__ 3.2 forward 4.apply_rotary_pos_emb 4.1 rotate_half 4.2 apply_rotary_pos_emb 四.RoPE 代码实现 1.Q/K/V …

飞桨大模型套件:一站式体验,性能极致,生态兼容

在Wave Summit 2023深度学习开发者大会上&#xff0c;来自百度的资深研发工程师贺思俊和王冠中带来的分享主题是&#xff1a;飞桨大模型套件&#xff0c;一站式体验&#xff0c;性能极致&#xff0c;生态兼容。 大语言模型套件PaddleNLP 众所周知PaddleNLP并不是一个全新的模型…

腾讯云轻量2核4G5M可容纳多少人访问?

腾讯云2核4G5M服务器支持多少人在线访问&#xff1f;卡不卡&#xff1f;腾讯云轻量2核4G5M带宽服务器支持多少人在线访问&#xff1f;5M带宽下载速度峰值可达640KB/秒&#xff0c;阿腾云以搭建网站为例&#xff0c;假设优化后平均大小为60KB&#xff0c;则5M带宽可支撑10个用户…

ad5665r STM32 GD32 IIC驱动设计

本文涉及文档工程代码&#xff0c;下载地址如下 ad5665rSTM32GD32IIC驱动设计,驱动程序在AD公司提供例程上修改得到,IO模拟的方式进行IIC通信资源-CSDN文库 硬件设计 MCU采用STM32或者GD32,GD32基本上和STM32一样,针对ad566r的IIC时序操作是完全相同的. 原理图设计如下 与MC…