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

C++ 学习(1)---- 左值 右值和右值引用

这里写目录标题

      • 左值
      • 右值
      • 左值引用和右值引用
      • 右值引用和移动构造函数
        • std::move 移动语义
        • 返回值优化
        • 移动操作要保证安全
      • 万能引用
      • std::forward 完美转发
          • 传入左值
          • 传入右值

左值

左值是指可以使用 & 符号获取到内存地址的表达式,一般出现在赋值语句的左边,比如变量、数组元素和指针等。

下面是左值的举例:

int i = 42; // i 是一个 left value
int *p = &i // i 是一个左值,可以通过 & 获取内存地址
int& ldemoFoo() {return i;
}ldemoFoo() = 42;//  这里使用函数返回引用的形式, ldemoFoo 是一个左值
int *p1 = &ldemoFoo() 

左值(lvalue)代表的是对象的身份和它在内存中的位置,它一般出现在赋值语句的左侧,左值通常是可修改的(可以修改的左值)。
Notice : 左值代表一个具体的对象位置,在内存中有明确位置,通常是可以修改的

左值的特点包括:

  • 可寻址性:左值可以取得地址,即你可以使用 & 运算符来取得一个左值的地址。
  • 可赋值性:左值可以出现在赋值语句的左侧,你可以将一个值赋给它。

下面类型的值都是左值:

  • 变量名:如 int x;,x是一个左值。
  • 数组元素:如 arr[0],arr[0] 是一个数组的左值元素。
  • 结构体或类的成员:如 obj.member ,obj.member 是一个对象的左值成员。
  • 解引用的指针:如 *ptr,*ptr 是通过指针访问的对象的左值。

并非所有的左值都是可修改的。例如,const 限定的左值就不应该被修改。

右值

在C++中,右值(rvalue)是指哪些不代表内存的中具体位置, 不能被赋值和取地址的值 。
一般出现在赋值操作符的右边,表达式结束就不存在的临时变量。

右值的典型例子包括 字面量、临时对象以及某些表达式的结果。
右值主要用来表示数据值本身,而不是数据所占据的内存位置。

右值的关键特性包括:

  • 不可寻址性:右值不能取得地址,尝试对右值使用 & 运算符会导致编译错误。
  • 可移动性: 由于右值不代表持久的内存位置,因此可以安全地 “移动” 它们的资源到另一个对象,而无需进行复制。这就是为什么右值经常与移动语义一起使用。
  • 临时性:许多右值是临时对象,它们在表达式结束后就会被销毁。

Notice:右值必须要有一个包含内存地址变量去接收这个指,否则就会丢弃

C++ 中右值的例子有:

  • 字面量:比如整数10、字符’A’、浮点数3.14。
  • 函数返回的临时值:如 getRandomNumber() 返回的随机数,注意函数也有可能返回左值。
  • 由运算符产生的值:比如表达式 a + b 的结果,假设 a 和 b 是数值类型的变量。
  • 空指针常量:nullptr。
  • 字符串字面量:比如"hello world"。
  • 类的右值构造函数或移动构造函数生成的临时对象:如 MyClass() 创建的临时对象。
  • 通过std::move()转换得到的右值:std::move(myObject),其中 myObject 是一个左值。
  • 数组下标的表达式:如果数组是右值,那么数组下标也是右值,例如 arr[0],其中arr是一个临时数组。
  • 类成员的右值访问:如果类有一个返回右值的成员函数,那么该函数返回的结果是右值。
// 10 'A' 都是右值字面量
int a = 10;
char b = 'A';//  generateResult 返回值是一个临时对象,也就是右值
int a = generateResult(20, 10);
// a + b 产生的结果也是右值
int m = a + b;
// "hello world "是右值
const char *pName = "hello world";
// nullptr 是右值
int32_t *p = nullptr;DemoClass p = DemoClass();

注意函数返回值不一定只能是右值,也有可能是左值,比如返回引用的情形

int& testlvaluefuncyion() {int i;return i;
}int testrvaluefuncyion() {int i = 5;return i;
}{testlvaluefuncyion();// 正确,函数返回值可作为左值testlvaluefuncyion() = 10;int *p1 = &testlvaluefuncyion();std::cout << "function return value as leftvalue" << std::endl;
}// 函数返回值是int类型,此时只能作为右值
{testrvaluefuncyion();//testrvaluefuncyion() = 10;std::cout << "function return value as rightvalue" << std::endl;
}

左值引用和右值引用

C++中的引用是一种别名,代表的就是变量的地址本身,可以通过一个变量别名访问一个变量的值。
int &a = b 表示可以通过引用 a 访问变量 b , 注意引用实际就是指向变量 b,等于是变量 b 的别名
左值引用是指对左值进行引用的引用类型,通常使用 & 符号定义
右值引用是指对右值进行引用的引用类型,通常使用 && 符号定义

C++11引入了右值引用,允许我们将右值绑定到引用上。这在 移动语义完美转发 等高级功能中非常有用。

class DemoClass {...};
// 接收一个左值引用
void foo(X& x);
// 接收一个右值引用
void foo(X&& x);X x;
foo(x); // 传入参数为左值,调用foo(X&);X bar();
foo(bar()); // 传入参数为右值,调用foo(X&&);

通过重载左值引用和右值引用两种函数版本,满足在传入左值和右值时触发不同的函数分支。 注意 void foo(const X& x); 同时接受左值和右值传参。

void foo(const X& x);
X x;
foo(x); // ok, foo(const X& x)能够接收左值传参X bar();
foo(bar()); // ok, foo(const X& x)能够接收右值传参// 新增右值引用版本
void foo(X&& x);
foo(bar()); // ok, 精准匹配调用foo(X&& x)

定义右值引用的方法:

int a = 10;
// 定义左值引用
int &lvalue_ref = a; // 定义右值引用
int &&rvalue_ref = 10 + 20; 

右值引用和移动构造函数

假设定义一个类 DemoContainerClass,包含一个指针成员变量 p,该指针指向了另一个成员变量 DemoBasicClass,假设 DemoBasicClass 占用了很大的内存,创建和复制 DemoBasicClass 都需要很大的开销。

class DemoBasicClass {
public:DemoBasicClass() {std::cout << __FUNCTION__ "construct call" << std::endl;}~DemoBasicClass() = default;DemoBasicClass(const DemoBasicClass& ref) {std::cout << __FUNCTION__ "copy construct call" << std::endl;}
};class DemoContainerClass{
private:DemoBasicClass *p = nullptr;
public:DemoContainerClass() {p = new DemoBasicClass();}
~DemoContainerClass() {if( p != nullptr) {delete p;}
}
DemoContainerClass(const DemoContainerClass& ref) {std::cout << __FUNCTION__ "copy construct call" << std::endl;p = ref.p;
}DemoContainerClass& operator=(const DemoContainerClass& ref) {std::cout << __FUNCTION__ "operator construct call" << std::endl;DemoBasicClass* tmp = new DemoBasicClass(*ref.p);delete this->p;this->p = tmp;return *this;
}

上面定义了 DemoContianerClass 的赋值构造函数 ,现在假设有下面的场景

{DemoContainerClass p;DemoContainerClass q;p = q;
}

输出如下:

rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =operator construct call
rValurRefDemo::DemoBasicClass::DemoBasicClasscopy construct call

DemoContainerClass pDemoContainerClass q 初始化时,都会执行 new DemoBasicClass,所以会调用两次 DemoBasicClassconstruct ,执行 p = q 时,会调用一次 DemoBasicClass 的拷贝构造函数,根据 ref 复制出一个新结果。
由于 q 在后面的场景还是可能使用的,为了避免影响 q,在赋值的时候调用DemoBasicClass 的构造函数复制出一个新的 DemobasicClass 给 p 是没有问题的。

但在下面的场景下,这样是没有必要的

static rValurRefDemo::DemoContainerClass demofunc() {return rValurRefDemo::DemoContainerClass();
}{DemoContainerClass p;p = demofunc();
}

这种场景下,demofunc 创建的那个临时对象在后续的代码中是不会用到的,所以我们不需要担心赋值函数中会不会影响到那个 DemobasicClass 临时对象,也就没有必要创建一个新的 DemoBasicClass 类型给 p,
更高效的做法是,直接使用 swap 交换对象的 p 指针,这样做有两个好处:

  1. 不需要调用 DemobasiClass 的构造函数,提高效率
  2. 交换之后,demofunc 返回的临时对象拥有 p 对象的 p 指针,在析构时可以自动回收,避免内存泄漏

这种避免高昂的复制成本,从而直接将资源从一个对象移动到另一个对象的行为,就是C++的 移动语义
哪些场景适合移动操作呢?无法获取内存地址的右值就很合适,我们不需要担心后续的代码会用到这个值。
添加移动赋值构造函数如下:

DemoContainerClass& operator=(DemoContainerClass&& rhs) noexcept {std::cout << __FUNCTION__ "move construct call" << std::endl;std::swap(this->p, rhs.p);return *this;
};

输出结果如下:

###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call
std::move 移动语义

C++提供了std::move函数,这个函数做的工作很简单:通过隐藏掉入参的名字,返回对应的右值。

std::cout << "#############################################" << std::endl;
{DemoContainerClass p;DemoContainerClass q;// OK 返回右值,调用移动赋值构造函数 q,但是 q 以后都不能正确使用了p = std::move(q);}std::cout << "#######################################" << std::endl;
{DemoContainerClass p;// OK 返回右值,调用移动赋值构造函数 效果和 demofunc 一样p = std::move(demofunc());
}

输出结果如下:

###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call
###############################################################
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoBasicClass::DemoBasicClassconstruct call
rValurRefDemo::DemoContainerClass::operator =move construct call

一个容易犯错的例子:

class Base {
public:// 拷贝构造函数Base(const Base& rhs);// 移动构造函数Base(Base&& rhs) noexcept;
};class Derived : Base {
public:Derived(Derived&& rhs)// wrong. rhs是左值,会调用到 Base(const Base& rhs).// 需要修改为Base(std::move(rhs)): Base(rhs) noexcept {...}
}
返回值优化

考虑下面的情形:

DemobasicClass foo() {DemobasicClass  x;return x;
};DemobasicClass  bar() {DemobasicClass  x;return std::move(x);
}

大家可能会觉得 foo 需要一次复制行为:从 x 复制到返回值;bar 由于使用了 std::move,满足移动条件,所以触发的是移动构造函数:从x移动到返回值。复制成本大于移动成本,所以 bar 性能更好。

实际效果与上面的推论相反,bar中使用std::move反倒多余了。现代C++编译器会有返回值优化。换句话说,编译器将直接在foo返回值的位置构造x对象,而不是在本地构造x然后将其复制出去。很明显,这比在本地构造后移动效率更快。

移动操作要保证安全

比较经典的场景是std::vector 扩缩容。当vector由于push_back、insert、reserve、resize 等函数导致内存重分配时,如果元素提供了一个 noexcept 的移动构造函数,vector 会调用该移动构造函数将元素移动到新的内存区域;否则 则会调用拷贝构造函数,将元素复制过去。

万能引用

完美转发是C++11引入的另一个与右值引用相关的高级功能。它允许我们在函数模板中将参数按照原始类型(左值或右值)传递给另一个函数,从而避免不必要的拷贝和临时对象的创建。

为了实现完美转发,我们需要使用 std::forward 函数和通用引用(也称为转发引用)。通用引用是一种特殊的引用类型,它可以同时绑定到左值和右值。通用引用的语法如下:

通用引用的形式如下:

template<typename T>
void foo(T&& param);

万能引用的ParamType是T&&,既不能是const T&&,也不能是std::vector&&

通用引用的规则有下面几条:

  1. 如果 expr 是左值, T 和 param 都会被推导为左值引用
  2. 如果 expr 是右值, T会被推导成对应的原始类型, param会被推导成右值引用(注意,虽然被推导成右值引用,但由于param有名字,所以本身还是个左值)。
  3. 在推导过程中,expr的const属性会被保留下来。

参考下面示例:

template<typename T>
void foo(T&& param);// x是一个左值
int x =2 7;
// cx 是带有const的左值
const int cx = x;
// rx 是一个左值引用
const int& rx = cx;// x是左值,所以T是int&,param类型也是int&
foo(x);// cx是左值,所以T是const int&,param类型也是const int&
foo(cx);// rx是左值,所以T是const int&,param类型也是const int&
foo(rx);// 27是右值,所以 T 是int,param类型就是 int&&
foo(27);

std::forward 完美转发

template<typename T, typename Arg> 
std::shared_ptr<T> factory_v4(Arg&& arg)
{ return std::shared_ptr<T>(new T(std::forward<Arg>(arg)));
}//  std::forward的定义如下
template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept
{return static_cast<S&&>(a);
}
传入左值

int p;
auto a = factory_v4§;

根据万能引用的推导规则,factory_v4中的 Arg 会被推导成 int&。这个时候factory_v4 和 std::forwrd等价于:

shared_ptr<A> factory_v4(int& arg)
{ return shared_ptr<A>(new A(std::forward<int&>(arg)));
}int& std::forward(int& a) 
{return static_cast<int&>(a);
}

这时传递给 A 的参数是 int&, 调用的是拷贝构造函数 A(int& ref), 符合预期

传入右值

auto a = factory_v4(3);

shared_ptr<A> factory_v4(int&& arg)
{ return shared_ptr<A>(new A(std::forward<int&&>(arg)));
}int&& std::forward(int&& a) 
{return static_cast<int&&>(a);
}

此时,std::forward作用与std::move一样,隐藏掉了arg的名字,返回对应的右值引用。
这个时候传给A的参数类型是X&&,即调用的是移动构造函数A(X&&),符合预期。
右值引用,移动构造
####重要参考
深浅拷贝和临时对象

相关文章:

C++ 学习(1)---- 左值 右值和右值引用

这里写目录标题 左值右值左值引用和右值引用右值引用和移动构造函数std::move 移动语义返回值优化移动操作要保证安全 万能引用std::forward 完美转发传入左值传入右值 左值 左值是指可以使用 & 符号获取到内存地址的表达式&#xff0c;一般出现在赋值语句的左边&#xff…...

Redis能保证数据不丢失吗?

引言 大家即使没用过Redis&#xff0c;也应该都听说过Redis的威名。 Redis是一种Nosql类型的数据存储&#xff0c;全称Remote Dictionary Server&#xff0c;也就是远程字典服务器&#xff0c;用过Dictionary的应该都知道它是一种键值对&#xff08;Key-Value&#xff09;的数…...

C++基础知识(六:继承)

首先我们应该知道C的三大特性就是封装、继承和多态。 此篇文章将详细的讲解继承的作用和使用方法。 继承 一个类&#xff0c;继承另一个已有的类&#xff0c;创建的过程 父类(基类)派生出子类(派生类)的过程 继承提高了代码的复用性 【1】继承的格式 class 类名:父类名 {}; 【…...

RM电控讲义【HAL库篇】(二)

8080并口模式是一种常见的计算机接口模式&#xff0c;主要用于LCD&#xff08;液晶显示屏&#xff09;模块。 在8080并口模式中&#xff0c;通信端口包括多种信号线&#xff0c;用于实现数据的读写和控制功能。主要的信号线包括&#xff1a; CS&#xff08;片选信号&#xff…...

Mac安装Appium

一、环境依赖 一、JDK环境二、Android-SDK环境&#xff08;android自动化&#xff09;三、Homebrew环境四、Nodejs 安装cnpm 五、安装appium六、安装appium-doctor来确认安装环境是否完成七、安装相关依赖 二、重头大戏&#xff0c; 配置wda&#xff08;WebDriverAgent&#x…...

数据库管理-第153期 Oracle Vector DB AI-05(20240221)

数据库管理153期 2024-02-21 数据库管理-第153期 Oracle Vector DB & AI-05&#xff08;20240221&#xff09;1 Oracle Vector的其他特性示例1&#xff1a;示例2 2 简单使用Oracle Vector环境创建包含Vector数据类型的表插入向量数据 总结 数据库管理-第153期 Oracle Vecto…...

通过傅里叶变换进行音频变声变调

文章目录 常见音频变声算法使用Wav库读写音频文件使用pitchShift算法进行音频变调主文件完整代码工程下载地址常见音频变声算法 在游戏或者一些特殊场景下为了提高娱乐性或者保护声音的特征,我们会对音频进行变声变调处理。常用的算法包括: 1.基于傅里叶变换的频域算法,该类…...

Opencv(C++)学习 ARM上引用opencv报相关头文件找不到

简单问题记录&#xff0c;C 与C互相引用时应该多注意类似问题。 问题描述&#xff1a;在项目中&#xff0c;建立了一个interface.h提供了一个C语言兼容的接口void work()&#xff0c;并在对应的interface.cpp中使用OpenCV完成相关处理实现。在PC端测试时&#xff0c;main.cpp成…...

中国服装行业ERP的现状与未来发展

随着全球数字化浪潮的兴起&#xff0c;中国服装行业也在不断探索数字化转型的路径&#xff0c;其中ERP&#xff08;企业资源计划&#xff09;系统作为管理和优化企业资源的重要工具&#xff0c;在服装行业中发挥着日益重要的作用。本文将探讨中国服装行业ERP的现状、作用&#…...

Unix与Linux区别

目录 历史和所有权 内核 发行版 开源性质 用户群体 命令行界面 历史和所有权 Unix&#xff1a; Unix是一个操作系统家族的名称&#xff0c;最早由贝尔实验室&#xff08;Bell Labs&#xff09;的肖像电机公司&#xff08;AT&T&#xff09;开发。最早的Unix版本是在19…...

惠尔顿 网络安全审计系统 任意文件读取漏洞复现

0x01 产品简介 惠尔顿网络安全审计产品致力于满足军工四证、军工保密室建设、国家涉密网络建设的审计要求&#xff0c;规范网络行为&#xff0c;满足国家的规范&#xff1b;支持1-3线路的internet接入、1-3对网桥&#xff1b;含强大的上网行为管理、审计、监控模块&#xff1b…...

Chrome插件(二)—Hello World!

本小节将指导你从头到尾创建一个基本的Chrome插件&#xff0c;你可以认为是chrome插件开发的“hello world”&#xff01; 以下详细描述了各个步骤&#xff1a; 第一步&#xff1a;设置开发环境 确保你拥有以下工具&#xff1a; 文本编辑器&#xff1a;如Visual Studio Cod…...

DBAPI如何使用数组类型参数

DBAPI如何使用数组类型参数 需求 根据多个id去查询学生信息 API创建 在基本信息标签&#xff0c;创建参数ids &#xff0c;参数类型选择 Array<bigint> 在执行器标签&#xff0c;填写sql&#xff0c;使用in查询 select * from student where id in <foreach ope…...

博途PLC PID仿真(单容水箱液位高度控制含变积分变增益测试)

单容水箱和双荣水箱的微分方程和数值求解,可以参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/131139432https://rxxw-control.blog.csdn.net/article/details/131139432这篇博客我们利用欧拉求解器在PLC里完成单容水箱的数学建模。PLC也可以和MATL…...

第1集《灵峰宗论导读》

《灵峰宗论》导读。诸位法师&#xff0c;诸位同学&#xff0c;阿弥陀佛&#xff01;&#xff08;阿弥陀佛&#xff01;&#xff09; 学人很高兴&#xff0c;这次有一个殊胜的因缘来跟大家共同地学习《灵峰宗论》的课程。《灵峰宗论》是蕅益大师一生当中在佛法的修学心得。在蕅…...

nuxt CSS 存放到独立文件进行SEO优化

nuxt CSS 到独立文件 在Nuxt.js中将CSS样式从单个文件分离为多个独立的文件可以通过使用extract-text-webpack-plugin来完成。 首先&#xff0c;确保已经安装了该插件&#xff1a; npm install extract-text-webpack-plugin --save-dev然后&#xff0c;在项目根目录下创建一个…...

台式电脑电源功率越大越费电吗?装机选购多少W电源

要组装一台电脑&#xff0c;我们首先需要选择硬件。 硬件搭配最关键的一点就是CPU和主板的兼容性。 硬件、电源等之间的平衡都需要仔细考虑。 那么台式电脑电源多大功率合适呢&#xff1f; 下面分享组装电脑电源瓦数选购指南&#xff0c;教您正确选择合适的电源瓦数。 让我们来…...

《TCP/IP详解 卷一》第4章 地址解析协议ARP

目录 4.1 引言 4.2 一个例子 4.3 ARP缓存 4.4 ARP帧格式 4.5 ARP例子 4.6 ARP缓存超时 4.7 代理ARP 4.8 免费ARP和地址冲突检测 4.9 ARP命令 4.10 使用ARP设置嵌入式设备IPv4地址 4.11 与ARP相关攻击 4.12 总结 4.1 引言 地址解析&#xff1a; IPv4&#xff1a;AR…...

如何在pgAdmin中用替换的值更新jsonb列?

我有一个名为files的PostgreSQL表&#xff0c;其中包括一个名为formats的jsonb表。虽然有些行是[null]&#xff0c;但其他行具有此结构的对象&#xff1a; {"thumbnail": {"ext": ".jpg","url": "https://some-url.com/image01.…...

冯诺依曼体系结构 计算机组成的金字塔

01 冯诺依曼体系结构&#xff1a;计算机组成的金字塔 学习计算机组成原理&#xff0c;到底是在学些什么呢&#xff1f;这个事儿&#xff0c;一两句话还真说不清楚。不过没关系&#xff0c;我们先从“装电脑”这个看起来没有什么技术含量的事情说起&#xff0c;来弄清楚计算机到…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

适应性Java用于现代 API:REST、GraphQL 和事件驱动

在快速发展的软件开发领域&#xff0c;REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名&#xff0c;不断适应这些现代范式的需求。随着不断发展的生态系统&#xff0c;Java 在现代 API 方…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

pycharm 设置环境出错

pycharm 设置环境出错 pycharm 新建项目&#xff0c;设置虚拟环境&#xff0c;出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...