《C++ Primer Plus》第18章:探讨 C++ 新标准(1)
本章首先复习前面介绍过的C++11功能,然后介绍如下主题:
- 移动语义和右值引用。
- Lambda 表达式。
- 包装器模板 function。
- 可变参数模板。
本章重点介绍 C++11 对 C++ 所做的改进。本书前面介绍过多项 C++11 功能,本章首先复习这些功能,并详细介绍其他一些功能。然后,指出一些超出了本书范围的 C++11 新增功能(考虑到 C++11 草案的篇幅比 C++98 长 98%,本书无法全面介绍)。最后,将简要地探讨 BOOST 库。
复习前面介绍过的 C++11 功能
本书前面介绍过很多 C++11 改进,但您现在可能忘了,本节简要地复习这些改进。
文章目录
- 复习前面介绍过的 C++11 功能
- 新类型
- 统一的初始化
- 声明
- 智能指针
- 异常规范方面的修改
- 作用域内枚举
- 对类的修改
- 模板和 STL 方面的修改
- 右值引用
新类型
C++11 新增了类型 long long 和 unsigned long long,以支持 64 位(或更宽)的整型;新增了类型 char16_t 和 char32_t,以支持 16 位和 32 位的字符表示;还新增了“原始”字符串。第3章讨论了这些新增的类型。
统一的初始化
C++11 扩大了用大括号括起的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户定义的类型(即类对象)。使用初始化列表时,可添加等号(=),也可不添加:
int x = {5};
double y {2.75};
short quar[5] {4, 5, 2, 76, 1};
另外,列表初始化语法也可用于 new 表达式中:
int * ar = new int [4] {2, 4, 6, 7}; // C++11
创建对象时,也可使用大括号(而不是园括号)括起的列表来调用构造函数:
class Stump {
private:int roots;double weight;
public:Stump(int r, double w) : roots(r), weight(w) {}
};
Stump s1(3,15.6); // old style
Stump s2{5, 43.4}; // C++11
Stump s3 = { 4, 32.1}; // C++ 11
然而,如果类有将模板 std::initializer_list 作为参数的构造函数,则只有该构造函数可以使用列表初始化形式。
-
缩窄
初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量。常规初始化允许程序执行可能没有意义的操作:
char c1 = 1.57e27; // double-to-char, undefined behavior char c2 = 459585821; // int-to-char, out of range, compile-time error然而,如果使用初始化列表语法,编译器将禁止进行这样的类型转换,即将值存储到比它“窄”的变量中:
char c1 {1.57e27}; // double-to-char, compile-time error char c2 = {459585821}; // int-to-char, out of range, compile-time error但允许转换为更宽的类型。
另外,只要值在较窄类型的取值范围内,将其转换为较窄的类型也是允许的:
char c1 {66}; // int-to-char, in range, allowed double c2 = {66}; // int-to-double, allowed -
initializer_list
C++11 提供了模板类 initializer_list,可将其用作构造函数的参数,这在第16章讨论过。如果类有接受 initializer_list 作为参数,则初始化列表就只能用于该构造函数。列表中的元素必须是同一种类型或可转换为同一种类型。STL 容器提供了将 initializer_list 作为参数的构造函数:
vector<int> a1(10); // uninitialized vector with 10 elements vector<int> a2{10}; // initializer-list, a2 has 1 element set to 10 vector<int> a3{4,6,1}; // 3 elements set to 4,6,1头文件 initializer_list 提供了对模板类 initializer_list 的支持。这个类包含成员函数 begin() 和 end(),可用于获悉列表的范围。除用于构造函数外,还可将 initializer_list 用作常规函数的参数:
#include<initializer_list> double sum(std::initializer_list<double> il); int main() {double total = sum({2.5,3.1,4}); // 4 converted to 4.0... }double sum(std::initializer_list<double> il) {double tot = 0;for (auto p = il.begin(); p!=il.end(); p++){tot += *p;}return tot; }
声明
C++11 提供了多种简化声明的功能,尤其在使用模板时。
-
auto
以前,关键字 auto 是一个存储类型说明符,C++11 将其用于实现自动类型推断。这要求进行显式初始化,让编译器能够将变量的类型设置为初始值的类型:auto maton = 112; // maton is type int auto pt = &maton; // pt is type int * double fm(double, int); auto pf = fm; // pf is type double(*) (double, int)关键字 auto 还可以简化模板声明。例如,如果 il 是一个 std::initializer_list<double>对象,则可将下述代码:
for(std::intializer_list<double>::iterator p = il.begin(); p != il.end(); p++ )替换为如下代码:
for (auto p = il.begin(); p != il.end(); p++ ) -
decltype
关键字 decltype 将变量的类型声明为表达式指定的类型。下面的语句含义是,让 y 的类型与 x 相同,其中 x 是一个表达式:
decltype(x) y;下面是几个示例:
double x; int n; decltype(x*n) q; // q same type as x*n, i.e., double decltype(&x) pd; // pd same as &x, i.e., double *这在定义模板时特别有用,因为只有等到模板被实例化时才能确定类型:
template<typename T, typename U> void ef(T t, U u) {decltype(T*U) tu;... }其中 tu 将为表达式 T*U 的类型,这里假定定义了运算 T*U。例如,如果 T 为 char,U 为 short,则 tu 将为 int,这是由整型算术自动执行整型提升导致的。
decltype 的工作原理比auto 复杂,根据使用的表达式,指定的类型可以为引用和 const。下面是几个示例:
int j = 3; int &k = j; const int &n = j; decltype(n) i1; // i1 type const int & decltype(j) i2; // i2 type int decltype((j)) i3; // i3 type int & decltype(k + 1) i4; // i4 type int有关导致上述结果的规则的详细信息,请参阅第8章。
-
返回类型后置
C++11 新增了一种函数声明语法:在函数名和参数列表后面(而不是前面)指定返回类型:
double f1(double, int); // traditional syntax auto f2(double, int) -> double; // new syntax, return type is double就常规函数的可读性而言,这种新语法好像是倒退,但让您能够使用 decltype 来指定模板函数的返回类型:
template<typename T, typename U> auto eff(T t, U u) -> decltype(T*U) { ... }这里解决的问题是,在编译器遇到 eff 的参数列表前,T 和 U 还不在作用域内,因此必须在参数列表后使用 decltype。这种新语法使得能够这样做。
-
模板别名: using =
对于冗长或复杂的标识符,如果能够创建其别名将很方便。以前,C++ 为此提供了 typedef:typedef std::vector<std::string>::iterator itType;C++ 提供了另一种创建别名的语法,这在第14章讨论过:
using itType = std::vector<std::string>::iterator;差别在于,新语法也可用于模板部分具体化,但 typedef不能:
template<typename T> using arr12 = std::array<T, 12>; // template for multiple aliases上述语句具体化模板 array<T, int> (将参数 int 设置为 12)。例如,对于下述声明:
std::array<double, 12> a1; std::array<std::string, 12> a1;可将它们替换为如下声明:
arr12<double> a1; arr12<std::string> a2; -
nullptr
空指针是不会指向有效数据的指针。以前,C++ 在源代码中使用 0 表示这种指针,但内部表示可能不同。这带来了一些问题,因为这使得 0 既可以表示指针常量,又可表示整型常量。正如第 12 章讨论的,C++11 新增了关键字 nullptr,用于表示空指针;它是指针类型,不能转换为整数类型。为向后兼容,C++11 仍允许使用 0 来表示空指针,因此表达式 nullptr == 0 为 true,但使用 nullptr 而不是 0 提供了更高的类型安全。例如,可将 0 传递给接受 int 参数的函数,但如果您试图将 nullptr 传递给这样的函数,编译器将此视为错误。因此,处于清晰和安全考虑,请使用 nullptr——如果您的编译器支持它。
智能指针
如果在程序中使用 new 从堆(自由存储区)分配内存,等到不再需要时,应使用 delete 将其释放。C++ 引入了智能指针 auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用 STL时)表明,需要有更精致的机制。基于程序员的编程体验和 BOOST 库提供的解决方案,C++11 摒弃了 auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr 和 weak_ptr,第 16 章讨论了前两种。
所有新增的智能指针都能与 STL 容器和移动语义协同工作。
异常规范方面的修改
以前,C++ 提供了一种语法,可用于指出函数可能引发哪些异常(参见第15章):
void f501(int) throw(bad_dog); // can throw type bad_dog exception
void f733(long long) throw(); // doesn't throw an exception
与 auto_ptr 一样, C++ 编程社区的集体经验表明,异常规范的效果没有预期的好。因此,C++11 摒弃了异常规范。然而,标准委员会认为,指出函数不会引发异常有一定的价值,他们为此添加了关键字 noexcept:
void f875(short, short) noexcept; // doesn't throw an exception
作用域内枚举
传统的 C++ 枚举提供了一种创建名称常量的方式,但其类型检查相当低级。另外,枚举名的作用域为枚举定义所属的作用域,这意味着如果在同一个作用域内定义两个枚举,它们的枚举成员不能同名。最后,枚举可能不是可完全移植的,因为不同的实现可能选择不同的底层类型。为解决这些问题,C++新增了一种枚举。这种枚举使用 class 或 struct 定义:
enum Old1 {yes, not, maybe}; // traditional form
enum class New1 { never, sometime, often, always }; // new form
enum struct New2 { never, lever, sever }; // new form
新枚举要求进行显式限定,以免发生名称冲突。因此,引用特定枚举时,需要使用 New1::never 和 New2::never 等。更详细的信息请参阅第10章。
对类的修改
为简化和扩展类设计,C++11 作了多项改进。这包括允许构造函数被继承和彼此调用、更佳的方法访问控制方式以及移动构造函数和移动赋值运算符,这些都将在本章介绍。下面先来复习本书前面介绍过的改进。
-
显式转换运算符
有趣的是,C++ 很早就支持对象自动转换。但随着编程经验的积累,程序员逐渐认识到,自动类型转换可能导致意外转换的问题。为解决这种问题,C++ 引入了关键字 explicit,以禁止单参数构造函数导致的自动转换:
class Plebe {Plebe(int); // automatic int-to-plebe conversionexplicit Plebe(double); // requires explicit use... }; Plebe a, b; a = 5; // implicit concersion, call Plebe(5) b = 0.5; // not allowed b = Plebe(0.5); // explicit conversionC++11 扩展了 explicit 的这种用法,使得可对转换函数做类似的处理(参见第11章):
class Plebe{ ... // conversion functionsoperator int() const;explicit operator double() const;... }; Plebe a, b; int n = a; // int-to-Plebe automatic conversion double x = b; // not allowed x = double(b); // explicit conversion, allowed -
类内成员初始化
很多首次使用 C++ 的用户都会问,为何不能在类定义中初始化成员?现在可以这样做了,其语法类似于下面这样:class Session {int mem1 = 10; // in-class initializationdouble mem2 {1966.54}; // in-class initializationshort mem3; public:Session(){} // #1Session(short s) : mem3(s) {} // #2Session(int n, double d, short s) : mem1(n), mem2(d), mem3(s) {} // #3... };可使用等号或大括号版本的初始化,但不能使用圆括号版本的初始化。其结果与给前两个构造函数提供成员初始化列表,并指定 mem1 和 mem2 的值相同:
Session() : mem1(10), mem2(1966.54) {} Session(short s) : mem1(10), mem2(1966.54), mem3(s) {}通过使用类内初始化,可避免在构造函数中编写重复的代码,从而降低了程序员的工作量、厌倦情绪和出错的机会。
如果构造函数在成员初始化列表中提供了相应的值,这些默认值将被覆盖,因此第三个构造函数覆盖了类内成员初始化。
模板和 STL 方面的修改
为改善模板和标准模板库的可用性,C++11 做了多个改进;有些是库本身,有些与易用性相关。本章前面提高了模板别名和适用于 STL 的智能指针。
-
基于范围的 for 循环
对于内置数组以及包含方法 begin() 和 end() 的类(如 std::string) 和 STL 容器,基于范围的 for 循环(第5章和第16章讨论过)可简化为它们编写循环的工作。这种循环对数组或容器中的每个元素执行指定的操作:
double prices[5] = { 4.99, 10.99, 6.87, 7.99, 8.49 }; for (double x : prices) {std::cout << x << std::endl; }其中,x 将依次为 prices 中每个元素的值。x 的类型应与数组元素的类型匹配。一种更容易、更安全的方式是,使用 auto 来声明 x,这样编译器将根据 prices 声明中的信息来推断 x 的类型:
double prices[5] = { 4.99, 10.99, 6.87, 7.99, 8.49 }; for (auto x : prices) {std::cout << x << std::endl; }如果要在循环中修改数组或容器的每个元素,可使用引用类型:
std::vector<int> vi(6); for (auto & x : vi) // use a reference if loop alters contentsx = std::rand(); -
新的 STL 容器
C++11 新增了 STL 容器 foward_list、unordered_map、unordered_multimap、unordered_set 和 unordered_multiset(参见第16章)。容器 forward_list 是一种单向链表,只能沿一个方向遍历;与双向链接的 list 容器相比,它更简单,在占用存储空间方面更经济。其他四种容器都是使用哈希表实现的。
C++11 还新增了模板 array(这在第4和第16章讨论过)。要实例化这种模板,可指定元素类型和固定的元素数:
std::array<int, 360> ar; // array of 360 ints这个模板类没有满足所有的常规模板需求。例如,由于长度固定,您不能使用任何修改容器大小的方法,如 put_back()。但 array 确实有方法 begin() 和 end(),这让您能够对 array 对象使用众多基于范围的 STL 算法。
-
新的STL方法
C++11新增了 STL 方法 cbegin() 和 cend()。与 begin() 和 end() 一样,这些新方法也返回一个迭代器,指向容器的第一个元素和最后一个元素的后面,因此可用于指定包含全部元素的区间。另外,这些新方法将元素视为 const。与此类似,crbegin() 和 crend() 是 rbegin() 和 rend() 的 const 版本。
更重要的是,除传统的复制构造函数和常规赋值运算符外,STL 容器现在还有移动构造函数和移动赋值运算符。移动语义将在本章后面介绍。
-
valarray 升级
模板 valarray 是独立于 STL 开发的,其最初的设计导致无法将基于范围的 STL 算法用于 valarray 对象。C++11 添加了两个函数(begin() 和 end() ),它们都接受 valarray 作为参数,并返回迭代器,这些迭代器分别指向 valarray 对象的第一个元素和最后一个元素后面。这让您能够将基于范围的 STL 算法用于 valarray。
-
摒弃 export
C++98 新增了关键字 export,旨在提供一种途径,让程序员能够将模板定义放在接口和实现文件中,其中前者包含原型和模板声明,而后者包含模板函数和方法的定义。实践证明这不现实,因此 C++11 终止了这种用法,但仍保留了关键字 export,供以后使用。
-
尖括号
为避免与运算符 >> 混淆,C++ 要求在声明嵌套模板时使用空格将尖括号分开:
std::vector<std::list<int> > v1; // >> not okC++ 11不再这样要求:
std::vector<std::list<int>> v1; // >> ok in C++11
右值引用
传统的 C++ 引用(现在称为左值引用)使得标识符关联到左值。左值是一个表示数据的表达式(如变量名或解除引用的指针),程序可获取其地址。最初,左值可出现在赋值语句的左边,但修饰符 const 的出现使得可以声明这样的标识符,即不能给它赋值,但可获取其地址:
int n;
int * pt = new int;
const int b = 101; // can't assign to b, but &b is valid
int & rn = n; // n identifies datum at address &n
int & rt = *pt; // *pt identifies datum at address pt
const int & rb = b; // b idendifies const datum at address &b
C++11 新增了右值引用(这在第8章讨论过),这是使用&&表示的。右值引用可关联到右值,即可出现在赋值表达式右边,但不能对其应用地址运算符的值。右值包括字面常量(C-风格字符串除外,它表示地址)、诸如 x+y 等表达式以及返回值的函数(条件是该函数返回的不是引用):
int x = 10;
int y = 20;
int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt(2.0);
注意:r2 关联到的是当时计算 x + y 得到的结果。也就是说,r2 关联到的是 23,即使以后修改了 x 或 y,也不会影响到 r2。
有趣的是,将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。也就是说,虽然不能将运算符 & 用于 13,但可将其用于 r1。通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。
下面的程序是一个简短的示例,演示了上述有关右值引用的要点。
// rvref.cpp -- simple uses of rvalue references
#include<iostream>inline double f(double tf) {return 5.0 * (tf-32.0)/9.0;
}int main(){using namespace std;double tc = 21.5;double && rd1 = 7.07;double && rd2 = 1.8 * tc + 32.0;double && rd3 = f(rd2);cout << " tc value and address: " << tc << ", " << &tc << endl;cout << "rd1 value and address: " << rd1 << ", " << &rd1 << endl;cout << "rd2 value and address: " << rd2 << ", " << &rd2 << endl;cout << "rd3 value and address: " << rd3 << ", " << &rd3 << endl;cin.get();return 0;
}
该程序的输出如下:
tc value and address: 21.5, 0x7ffdd471ca40
rd1 value and address: 7.07, 0x7ffdd471ca30
rd2 value and address: 70.7, 0x7ffdd471ca20
rd3 value and address: 21.5, 0x7ffdd471ca10
引入右值引用的主要目的之一是实现移动语义,这是本章将讨论的下一个主题。
相关文章:
《C++ Primer Plus》第18章:探讨 C++ 新标准(1)
本章首先复习前面介绍过的C11功能,然后介绍如下主题: 移动语义和右值引用。Lambda 表达式。包装器模板 function。可变参数模板。 本章重点介绍 C11 对 C 所做的改进。本书前面介绍过多项 C11 功能,本章首先复习这些功能,并详细…...
PCB板漏孔、漏槽怎么办?看工程师避坑“SOP”
本文为大家介绍PCB画板时常见的钻孔问题,避免后续踩同样的坑。钻孔分为三类,通孔、盲孔、埋孔。不管是哪种孔,孔缺失的问题带来的后果是直接导致整批产品不能使用。因此钻孔设计的正确性尤为重要。 案例讲解 问题1:Altium设计的文…...
mysql数据库同步方案:springboot+集成cannal
1授权 -- 使用命令登录:mysql -u root -p -- 创建用户 用户名:canal 密码:Canal123456 create user canal% identified by Canal123456; -- 授权 *.*表示所有库 grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to canal% ident…...
oracle 19c 创建物化视图并测试logminer进行日志挖掘
1、创建物化视图 alter session set containerpdb; grant create materialized view to scott; create materialized view 物化视图名 -- 1. 创建物化视图 build [immediate | deferred] -- 2. 创建方式,默认 immediate refre…...
2.1 黑群晖驱动:10代u核显硬解驱动(解决掉IP、重启无法连接问题)
本文提供了两种10代核显驱动方式:1)第一种(本文:二、仅修改i915.ko驱动10代u核显方法)为网上流传最多但是对主板兼容性要求很高,网上评论常会出现操作后无法识别IP(掉IP)的问题。因此,采用第一种…...
二、CSS
一、CSSHTML的结合方式 1、第一种:在标签的style属性上设置"key:value value;",修改标签样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title>…...
变分推断 (Variational Inference) 解析
前言 如果你对这篇文章可感兴趣,可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。 变分推断 在贝叶斯方法中,针对含有隐变量的学习和推理,通常有两类方式,其一是马尔可…...
27. 移除元素
题目链接:https://leetcode.cn/problems/remove-element/给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输…...
hive临时目录清理
hive运行失败会导致临时目录无法自动清理,因此需要自己写脚本去进行清理 实际发现hive临时目录有两个: /tmp/hive/{user}/* /warehouse/tablespace//hive/**/.hive-staging_hive 分别由配置hive.exec.scratchdir和hive.exec.stagingdir决定: 要注意的…...
如何创建发布新品上市新闻稿
推出新产品对任何企业来说都是一个激动人心的时刻,但向潜在客户宣传并围绕您的新产品引起轰动也可能是一个挑战。最有效的方法之一就是通过发布新品上市新闻稿。精心制作的新闻稿可以帮助我们通过媒体报道、吸引并在目标受众中引起关注。下面,我们将讲述…...
关于.bashrc和setup.bash的理解
在创建了ROS的workspace后,需要将workspace中的setup.bash文件写入~/.bashrc 文件中,让其启动: source /opt/ros/melodic/setup.bash这句话的目的就是在开新的terminal的时候,运行这个setup.bash,而这个setup.bash的作…...
03 Android基础--fragment
03 Android基础--fragment什么是fragment?fragment生命周期?动态的fragment与静态的fragmentfragment常用的两个类与APIFragment与Activity通信什么是fragment? 碎片,一个activity中可以使用多个fragment,可以把activi…...
Redis使用,AOF、RDB
前言 如果有人问你:"你会把 Redis 用在什么业务场景下?" 我想你大概率会说:"我会把它当作缓存使用,因为它把后端数据库中的数据存储在内存中,然后直接从内存中读取数据,响应速度会非常快。…...
SOLIDWORKS Premium 2023 SP1.0 三维设计绘图软件
SOLIDWORKS 中文完美正式版提供广泛工具来处理最复杂的问题,并提供深层技术完成关键细节工作。新功能可助您改善产品开发流程,以更快地将创新产品投入生产。Solidworks 是达索公司最新推出的三维CAD系统,它可让设计师大大缩短产品的设计时间,让产品得以快速、高效地投向市场…...
PyQGIS开发--自动化地图布局案例
前言创建地图布局是 GIS 作业结束时的一项常见任务。 它用于呈现最终结果的输出,作为与用户交流的一种方式,以便从地图中获取信息、知识或见解。 在包括 QGIS 在内的任何 GIS 软件中制作地图布局都非常容易。 但另一方面,当我们必须生成如此大…...
严格模式和非严格模式下的this指向问题
一、全局环境 1.函数调用 非严格模式:this指向是Window // 普通函数 function fn () { console.log(this, this); } fn() // 自执行函数 (function fn () { console.log(this, this); })() 严格模式:this指向是undefined //…...
vue2、vue3组件传值,引用类型,对象数组如何处理
vue2、vue3组件传值,引用类型,对象数组如何处理 Excerpt 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父… 下述组件传值指引…...
165. 小猫爬山
Powered by:NEFU AB-IN Link 文章目录165. 小猫爬山题意思路代码165. 小猫爬山 题意 翰翰和达达饲养了 N只小猫,这天,小猫们要去爬山。 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕…...
ECharts教程(详细)
ECharts教程(详细) 非常全面的ECharts教程,非常全面的ECharts教程,目前线条/节点颜色、线条粗细、线条样式、线条阴影、线条平滑、线条节点大小、线条节点阴影、线条节点边框、线条节点边框阴影、工具提醒、工具提醒样式、工具自定义提醒、工具提醒背景…...
pinia
目录一、介绍二、快速上手1.安装2.基本使用与state3.actions的使用4.getters的使用5.storeToRefs的使用6.pinia模块化三、数据持久化1.安装2.使用插件3.模块开启持久化4.按需缓存模块的数据一、介绍 pinia从使用角度和之前Vuex几乎是一样的,比Vuex更简单了。 在Vu…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
