【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
文章目录
- C++类与对象
- 前言
- 读者须知
- RVO 与 NRVO 的启用条件
- 如何确认优化是否启用?
- 1. 按值传递与拷贝省略
- 1.1 按值传递的概念
- 1.2 示例代码
- 1.3 按值传递的性能影响
- 1.3.1 完全不优化
- 1.4 不同编译器下的优化表现
- 1.4.1 Visual Studio 2019普通优化
- 1.4.2 Visual Studio 2022激进优化
- 1.5 小结
- 2. 返回值优化(RVO)
- 2.1 RVO 的概念
- 2.2 示例代码
- 2.3 不同优化下的表现
- 2.3.1 完全不优化的情况
- 2.3.2 启用 RVO 的情况(Visual Studio 2019)
- 2.3.3 激进 RVO 的情况(Visual Studio 2022)
- 2.4 小结
- 3. 命名返回值优化(NRVO)
- 3.1 NRVO 的概念
- 3.2 示例代码
- 3.3 优化下的不同表现
- 3.3.1 完全不优化的情况
- 3.3.2 启用 NRVO 的情况(Visual Studio 2019 和 2022)
- 3.4 Visual Studio 2022 的优化对比
- 3.5 小结
- 4. 赋值操作无法优化的原因
- 4.1 赋值操作的本质
- 4.2 示例代码
- 5. Visual Studio 2019 vs Visual Studio 2022 编译器优化差异
- 5.1 编译器的工作原理
- 5.2 为什么 VS2022 更加激进?
- 5.3 编译器的激进优化总结
- 6. 总结
C++类与对象
前言
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!
C++ 作为一门底层高效语言,在设计时便考虑到了性能和资源管理。程序员在编写代码时,常常面临对象的频繁创建与销毁,尤其是在函数返回值的传递过程中,可能会触发多次对象的拷贝构造或移动操作。为了减少这些不必要的拷贝,C++ 编译器会采用一些优化技术,如 拷贝省略(Copy Elision)、返回值优化(Return Value Optimization,RVO)和 命名返回值优化(Named Return Value Optimization,NRVO)。
读者须知
RVO 与 NRVO 的启用条件
虽然 RVO 和 NRVO 是编译器自动完成的优化,但是这些优化并不总是启用,具体取决于编译器的实现和配置。例如:
- 在 C++17 之前,RVO 是一个可选优化,但在 C++17 标准之后,RVO 被强制启用,编译器必须在符合条件的情况下执行拷贝省略。
- NRVO 通常依赖于编译器的智能分析,虽然大多数现代编译器都能支持 NRVO,但其效果和激进程度因编译器和版本的不同而有所差异。
因此,尽管 RVO 是 C++ 标准的一部分,但 NRVO 则并不总是强制执行,尤其是在复杂场景下,不同的编译器版本可能表现出不同的优化行为。
如何确认优化是否启用?
你可以通过编译时的优化级别和编译器选项来控制 RVO 和 NRVO 的启用。通常使用 -O2
或 -O3
优化级别可以启用这些优化。如果你希望查看编译器具体是否执行了这些优化,可以通过以下方式进行检查:
- GCC:使用
-fno-elide-constructors
禁用拷贝省略。 - Clang:通过
-fno-elide-constructors
禁用拷贝省略。 - MSVC:Visual Studio 中可以通过
/Od
(禁用优化)或/O2
(启用优化)控制优化行为。
在本篇中主要使用VS2019和VS2022来进行比较,因为实际情况的复杂性,以及编译器版本的不同,甚至同一大版本中小版本的不同更新的VS都存在一定的差异,本篇输出结果示例仅作参考,更多的是让读者通过不同优化的比较来理解现代编译器在提升程序效率所做的改进
1. 按值传递与拷贝省略
1.1 按值传递的概念
在 C++ 中,按值传递意味着函数参数是通过创建实参对象的副本来传递的。通常会触发拷贝构造或移动构造函数。按值传递可以在函数内部修改参数副本,而不影响原始实参对象,但这也带来了额外的性能开销。
当我们传递一个对象给函数时,编译器会为这个对象创建一个副本。这个副本的创建需要调用 拷贝构造函数,并且在函数执行结束后,该副本会被销毁,从而调用 析构函数。这一过程涉及到内存的分配与释放,对于大对象而言,可能会导致性能下降。
1.2 示例代码
#include <iostream>
using namespace std;class A {
public:A(int a = 0) : _a1(a) {cout << "A(int a) 构造函数被调用, _a = " << _a1 << endl;}A(const A& aa) : _a1(aa._a1) {cout << "A(const A& aa) 拷贝构造函数被调用" << endl;}A(A&& aa) noexcept : _a1(aa._a1) {cout << "A(A&& aa) 移动构造函数被调用" << endl;}~A() {cout << "~A() 析构函数被调用" << endl;}private:int _a1;
};void f1(A aa) {} // 按值传递int main() {A aa1(10); // 创建对象 aa1f1(aa1); // 按值传递,调用拷贝构造return 0;
}
1.3 按值传递的性能影响
在上述代码中,按值传递会创建对象的副本,并调用 拷贝构造函数 或 移动构造函数,然后在函数执行结束时,析构函数将会被调用。这一过程虽然实现了副本的安全传递,但对于大型对象,频繁的拷贝和析构会导致性能问题。
1.3.1 完全不优化
在没有任何优化的情况下,按值传递时会创建一个对象的副本,并调用拷贝构造函数。返回对象后,析构函数将被调用两次:一次是为原对象,另一次是为副本。
输出结果:
A(int a) 构造函数被调用, _a = 10
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
解释:
- 对象
aa1
在主函数中通过构造函数被创建。 - 按值传递时,编译器调用了拷贝构造函数,为
aa1
创建了副本。 - 当函数
f1
执行结束后,副本被销毁,调用了析构函数。 - 当
main
函数结束时,原始对象aa1
也被销毁。
1.4 不同编译器下的优化表现
1.4.1 Visual Studio 2019普通优化
在 Visual Studio 2019 中,编译器在普通优化模式下,依然会调用拷贝构造函数。
输出结果:
A(int a) 构造函数被调用, _a = 10
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
尽管编译器启用了部分优化,但在这种按值传递的情况下,仍然需要调用拷贝构造函数,并最终调用两次析构函数。
1.4.2 Visual Studio 2022激进优化
VS2022 的优化更加激进,它能够跳过对象的拷贝构造,直接传递原始对象的引用。通过内存重用和别名优化,编译器可以避免创建副本。
输出结果:
A(int a) 构造函数被调用, _a = 10
~A() 析构函数被调用
解释:
- 在 VS2022 中,拷贝构造函数被优化掉,编译器直接将原对象
aa1
传递给函数f1
。说白了,就是编译器上下文分析发现函数里面aa
不会修改,那直接就在函数里面使用aa1
即可,此时函数里面的aa
就是aa1
的别名 - 无需创建副本,也不需要析构副本,只在
main
函数结束时销毁aa1
。
1.5 小结
- 按值传递通常会触发拷贝构造或移动构造,并在函数结束时触发析构函数。
- Visual Studio 2019 中,普通优化仍然会调用拷贝构造函数。
- Visual Studio 2022 的激进优化则可以跳过拷贝构造,避免副本的创建。
2. 返回值优化(RVO)
2.1 RVO 的概念
返回值优化(RVO) 是编译器的一种优化技术,它允许编译器在函数返回临时对象时,
直接在调用者的内存空间中构造该对象,避免不必要的拷贝或移动构造。
当函数返回一个局部临时对象时,通常会触发一次拷贝构造或移动构造,因为局部对象需要从函数内部复制到外部。然而,RVO 能够避免这种多余的拷贝或移动操作,编译器直接在调用者的内存空间中构造返回的对象-。
2.2 示例代码
A f2() {A aa(5);return aa; // 返回局部临时对象
}int main() {A a2 = f2(); // 接收返回值return 0;
}
2.3 不同优化下的表现
2.3.1 完全不优化的情况
在没有启用 RVO 的情况下,返回值会经历多次拷贝操作:
- 在
f2()
内部创建局部对象aa
。 - 创建一个临时对象,将
aa
拷贝到这个临时对象中。 - 最后将临时对象拷贝给
a2
,并调用两次拷贝构造函数。
输出结果:
A(int a) 构造函数被调用, _a = 5
A(const A& aa) 拷贝构造函数被调用
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
解释:
- 局部对象
aa
在f2
函数内创建,并通过两次拷贝构造传递给a2
。 - 三次析构函数分别销毁局部对象
aa
、临时对象和最终返回的a2
。
2.3.2 启用 RVO 的情况(Visual Studio 2019)
在 Visual Studio 2019 中,编译器启用了 RVO 优化,避免了创建临时对象,直接将aa
拷贝给a2
.
输出结果:
A(int a) 构造函数被调用, _a = 5
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
解释:
- 编译器避免了临时对象的创建,但仍通过拷贝构造将
aa
传递给a2
。 - 整个过程调用了一次拷贝构造,并在
a2
和aa
被销毁时分别调用析构函数。
2.3.3 激进 RVO 的情况(Visual Studio 2022)
Visual Studio 2022 实现了更加激进的 RVO 优化。编译器直接在 a2
的内存空间中构造对象 aa
,完全跳过拷贝构造。其实就是下文讲的NRVO
输出结果:
A(int a) 构造函数被调用, _a = 5
~A() 析构函数被调用
解释:
aa
直接在a2
的内存空间中构造,避免了临时对象和拷贝构造。- 最终只需要调用一次析构函数来销毁
a2
。
2.4 小结
- RVO 主要用于返回临时对象的优化,能够在返回局部对象时避免多次拷贝。
- Visual Studio 2019 中启用了 RVO,减少了临时对象的创建,但仍会调用一次拷贝构造。
- Visual Studio 2022 则更加激进,完全避免了拷贝构造,直接在返回对象的目标内存空间中构造该对象。
3. 命名返回值优化(NRVO)
3.1 NRVO 的概念
命名返回值优化(NRVO) 是 RVO 的扩展,专门用于优化函数返回命名局部变量的情况。编译器会在调用者的内存空间中直接构造该命名对象,避免临时对象和拷贝操作。
NRVO 允许编译器在返回函数内的命名局部变量时进行优化,直接在目标对象的内存中构造该局部变量,而不是创建一个临时对象进行拷贝或移动。这一优化虽然不像 RVO 那样是 C++ 标准的强制要求,但大多数现代编译器都会尝试实现这种优化。
3.2 示例代码
A f3() {A a(3);return a; // 返回命名局部变量
}int main() {A a2 = f3(); // 使用返回值return 0;
}
在这段代码中,函数 f3
返回命名局部变量 a
。没有 NRVO 优化的情况下,a
会首先被拷贝到一个临时对象中,然后该临时对象会被拷贝到 a2
。
3.3 优化下的不同表现
3.3.1 完全不优化的情况
在没有 NRVO 优化的情况下,返回的命名对象 a
会经历以下拷贝过程:
- 在
f3
函数内创建局部对象a
。 - 创建一个临时对象,将
a
拷贝到这个临时对象中。 - 最后将临时对象拷贝到
a2
中。
输出结果:
A(int a) 构造函数被调用, _a = 3
A(const A& aa) 拷贝构造函数被调用
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
解释:
- 局部变量
a
在f3
中创建,并通过两次拷贝构造传递给a2
。 - 由于没有启用 NRVO,因此返回值会触发两次拷贝构造和三次析构函数调用。
3.3.2 启用 NRVO 的情况(Visual Studio 2019 和 2022)
在 Visual Studio 2019 和 Visual Studio 2022 中,NRVO 技术的实现基本一致。局部对象 a
会直接在 a2
的内存空间中构造,没有临时对象和多余的拷贝操作。
输出结果:
A(int a) 构造函数被调用, _a = 3
~A() 析构函数被调用
解释:
- 通过 NRVO,编译器直接在
a2
的内存空间中构造局部对象a
,避免了拷贝构造。 - 整个过程只需要一次析构调用,销毁
a2
。
3.4 Visual Studio 2022 的优化对比
复杂场景中的 NRVO: Visual Studio 2022 在处理复杂的函数返回场景时,表现更为激进。例如在多层嵌套、条件判断等情况下,NRVO 依然有效,而某些编译器可能在复杂条件下无法实现优化。
以下是一个复杂的 NRVO 示例:
A f4(bool flag) {A a1(1);A a2(2);if (flag) {return a1;} else {return a2;}
}int main() {A a3 = f4(true); // 使用返回值return 0;
}
在这种复杂场景中,Visual Studio 2022 依然能够直接在 a3
的内存空间中构造返回值(无论是 a1
还是 a2
),而不会创建临时对象或额外的拷贝构造。并且这种情况下发现只需要返回a1
,那甚至可能会跳过a2
的创建
输出结果:
A(int a) 构造函数被调用, _a = 1
~A() 析构函数被调用
3.5 小结
- NRVO 针对命名局部变量的优化,能够在返回命名变量时避免临时对象和拷贝构造。
- Visual Studio 2019 和 2022 的 NRVO 实现基本一致,能够在大多数情况下避免拷贝构造。
- Visual Studio 2022 在处理复杂场景时的 NRVO 优化表现更为激进,即使在条件判断和嵌套场景中,也能有效避免额外的临时对象和拷贝。
4. 赋值操作无法优化的原因
4.1 赋值操作的本质
赋值操作与对象构造不同,它修改已经存在的对象,因此不能像RVO或NRVO那样进行优化。赋值操作必须真正执行对象状态的复制,无法通过跳过拷贝来优化。
在 C++ 中,赋值操作是将一个对象的内容复制到另一个对象中。这与对象的构造不同,因为在赋值操作时,目标对象已经存在,不能通过构造优化来避免对象的状态复制。
4.2 示例代码
A aa1(10);
A aa2(20);
aa1 = aa2; // 赋值操作
输出结果:
A(int a) 构造函数被调用, _a = 10
A(int a) 构造函数被调用, _a = 20
A& operator=(const A& aa) 赋值运算符被调用
~A() 析构函数被调用
~A() 析构函数被调用
解释:
- 对象
aa1
和aa2
分别通过构造函数创建。 - 赋值操作需要实际复制
aa2
的数据到aa1
中,因此必须调用赋值运算符。
赋值操作在 C++ 中并没有优化空间,因为在赋值操作时,目标对象已经存在,编译器必须实际执行数据复制的过程,而不能通过拷贝省略来进行优化。
5. Visual Studio 2019 vs Visual Studio 2022 编译器优化差异
5.1 编译器的工作原理
编译器在优化过程中,使用了别名分析和内存重用技术。在分析对象的使用模式后,编译器能够判断某些对象的拷贝是多余的,可以直接复用原始对象的内存地址。这种优化策略依赖于编译器对代码中对象生命周期的深层次分析。
5.2 为什么 VS2022 更加激进?
VS2022 能够在更多复杂场景下进行优化,包括跨行优化、多层函数调用等。这是因为编译器能够更好地理解对象的生命周期,并通过对象生命周期分析来跳过冗余的拷贝操作。
例如在以下代码中:
A f4() {A a1(1);A a2(2);return a1; // 返回局部变量
}int main() {A a3 = f4();return 0;
}
VS2019 的输出结果:
A(int a) 构造函数被调用, _a = 1
A(int a) 构造函数被调用, _a = 2
A(const A& aa) 拷贝构造函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
~A() 析构函数被调用
在 VS2019 中,即使返回的是局部变量,仍会创建一个临时对象,然后通过拷贝构造将其传递给 a3
。
VS2022 的输出结果:
A(int a) 构造函数被调用, _a = 1
~A() 析构函数被调用
在 VS2022 中,编译器能够更好地分析对象生命周期,跳过了临时对象的创建,直接在 a3
的内存空间中构造返回的局部变量 a1
。
5.3 编译器的激进优化总结
- Visual Studio 2019 在大部分情况下能够启用 RVO 和 NRVO,但在某些复杂场景下仍需要额外的拷贝构造。
- Visual Studio 2022 的优化更加激进,通过更好的对象生命周期分析,能够避免更多不必要的拷贝操作,即使在复杂的函数调用和条件判断中,仍能高效地进行返回值优化。
6. 总结
通过本文,我们深入分析了 C++ 中编译器优化的几个重要方面,包括 返回值优化(RVO) 和 命名返回值优化(NRVO)。这些优化能够显著减少对象的拷贝构造和临时对象的创建,从而提升程序的执行效率。
- RVO 主要用于优化返回临时对象的场景,Visual Studio 2022 通过激进优化完全跳过了拷贝构造。
- NRVO 则用于优化返回命名局部变量的场景,Visual Studio 2019 和 2022 的 NRVO 实现基本一致,但 2022 的编译器在复杂场景中的表现更为出色。
- 在涉及对象赋值的场景中,由于目标对象已经存在,因此无法通过 RVO 或 NRVO 进行优化。
现代编译器已经能够通过 别名分析 和 对象生命周期分析 实现高度智能的优化。程序员不需要显式地进行优化,只需合理设计函数返回结构,编译器会自动帮助完成优化。
如果你希望了解更多编译器优化的底层机制,可以查阅 cppreference RVO文档 和 MSVC优化指南。
以上就是关于C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️
相关文章:

【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
文章目录 C类与对象前言读者须知RVO 与 NRVO 的启用条件如何确认优化是否启用? 1. 按值传递与拷贝省略1.1 按值传递的概念1.2 示例代码1.3 按值传递的性能影响1.3.1 完全不优化 1.4 不同编译器下的优化表现1.4.1 Visual Studio 2019普通优化1.4.2 Visual Studio 202…...

什么时候用synchronized,什么时候用Reentrantlock
文章目录 使用 synchronized 的场景使用 ReentrantLock 的场景综合考虑 使用 synchronized 的场景 synchronized 是 Java 内置的同步机制,使用起来比较简单且常用于如下场景: 1、简单的同步逻辑:当你的同步逻辑非常简单,比如只需…...

[ffmpeg]音频格式转换
本文主要梳理 ffmpeg 中的音频格式转换。由于采集的音频数据和编码器支持的音频格式可能不一样,所以经常需要进行格式转换。 API 调用 常用 API struct SwrContext *swr_alloc(void); int swr_init(struct SwrContext *s); struct SwrContext *swr_alloc_set_opt…...

SSRF工具类-SsrfTool
为了帮助开发人员和安全研究人员检测和修复SSRF(Server-Side Request Forgery)漏洞,存在 多种工具。这里我将给出一个简单的工具类示例,这个工具类可以用来检查一个给定的URL是否可 能引发SSRF攻击。请注意,这个工具类主要用于教育目的,并不意味着它可以完全防止所有的…...

python集合运算介绍及示例代码
Python 中的集合(set)是一种数据类型,用于存储唯一元素的无序集合。集合支持多种运算,如并集、交集、差集和对称差集,方便执行数学上的集合操作。 1. 创建集合 可以使用大括号 {} 或者 set() 函数创建集合࿱…...

『功能项目』按钮的打开关闭功能【73】
本章项目成果展示 我们打开上一篇72QFrameWork制作背包界面UGUI的项目, 本章要做的事情是制作打开背包与修改器的打开关闭按钮 首先打开UGUICanvas复制button按钮 重命名为ReviseBtn 修改脚本:UIManager.cs 将修改器UI在UGUICanvas预制体中设置为隐藏 运…...

Linux 常用命令 - more 【分页显示文件内容】
简介 more 命令源自英文单词 more, 表示 “更多”,它是一个基于文本的程序,用于查看文本文件的内容。该命令会逐页显示文件内容,允许用户按页浏览大型文本文件。当用户完成当前页的阅读后,可以通过按键(空格键或回车键…...

Kotlin Android 环境搭建
Kotlin Android 环境搭建 1. 引言 Kotlin 已成为 Android 开发的官方语言之一,因其简洁、表达性强和易于维护的特点而受到广大开发者的喜爱。在本教程中,我们将详细介绍如何在您的计算机上搭建 Kotlin Android 开发环境。 2. 系统要求 在开始搭建 Kotlin Android 开发环境…...

常见协议及其默认使用的端口号
在网络通信中,端口号用于标识特定的应用程序或服务。IANA(Internet Assigned Numbers Authority)负责分配和管理这些端口号。端口号分为三个范围: 熟知端口(Well-Known Ports):0到1023…...

04-Docker常用命令
04-Docker常用命令 启动类命令 启动docker systemctl start docker停止docker systemctl stop docker重启docker systemctl restart docker查看docker状态 systemctl status docker开机启动docker systemctl enable docker帮助类命令 查看docker版本 docker version查…...

数字化转型中的供应链管理优化
在当今全球化和数字化的浪潮下,企业供应链管理面临着前所未有的挑战和机遇,企业在数字化转型过程中,如何优化供应链管理成为提升竞争力的关键。通过应用先进技术如RPA机器人流程自动化、大数据分析、物联网等,企业可以显著提高物流…...

【Python报错已解决】SyntaxError: invalid syntax
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

树上差分+lca 黑暗的锁链
//** 太久不写了,感觉很难受。。。比赛最近打得也不好,课内任务又重,还要忙着做项目。何去何从。 今天又写了一题,用了树上差分的知识。下面来整理整理。 1.首先让我们学一下lca(最小公共父节点) 我用的…...

opencv4.5.5 GPU版本编译
一、安装环境 1、opencv4.5.5 下载地址:https://github.com/opencv/opencv/archive/refs/tags/4.5.5.ziphttps://gitee.com/mirrors/opencv/tree/4.5.0 2、opencv-contrib4.5.5 下载地址:https://github.com/opencv/opencv_contrib/archive/refs/tags/4…...
线性跟踪微分器TD详细测试(Simulink 算法框图+SCL完整源代码)
1、ADRC线性跟踪微分器 ADRC线性跟踪微分器(ST+SCL语言)_adrc算法在博途编程中scl语言-CSDN博客文章浏览阅读784次。本文介绍了ADRC线性跟踪微分器的算法和源代码,包括在SMART PLC和H5U平台上的实现。文章提供了ST和SCL语言的详细代码,并讨论了跟踪微分器在自动控制中的作用…...

LabVIEW闪退
LabVIEW闪退或无法启动可能由多个原因引起,特别是在使用了一段时间后突然发生的问题。重启电脑后 LabVIEW 和所有 NI 软件都无法打开,甚至在卸载和重装时也没有反应。这种情况通常与系统环境、软件冲突或 NI 软件组件的损坏有关。 1. 检查系统和软件冲突…...

【WPF】03 动态生成控件
说明 今天记录一篇关于动态生成控件的方法,也是反复查了一些资料,逐步完善成自己需要的方法,感觉还是比较好用的。通过这个需求,在网上也找了一些资料,发现了一个开源图形UI组件HandyControl,觉得比较好&a…...

调试LTE模块碰到的4字节对齐问题
在调试LTE模块,有两个模块,碰到两种4字节对齐问题,其错误提示都是类似如下的内容: DWC_OTG: dwc_otg_hcd_urb_enqueue urb->transfer_buffer address not align to 4-byte 0xee419e8e 都是USB控制器处理的数据时需要4字节对齐…...

一篇讲完HTML核心内容
一、HTML 1、 HTML概念 网页,是网站中的一个页面,通常是网页是构成网站的基本元素,是承载各种网站应用的平台。通俗的说,网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件,俗称 HTML文件。 2、…...

2024icpc(Ⅱ)网络赛补题 G
2024icpc(Ⅱ)网络赛补题 G 题目链接:The 2024 ICPC Asia EC Regionals Online Contest (II) G、Game 题意: 给定Alice和Bob的每一轮的概率 p 0 , p 1 p_0, p_1 p0,p1 给定Alice和Bob的初始数字 x , y x,y x,y。 对于每一轮: 如果Al…...

AIGC时代!AI的“iPhone时刻”与投资机遇
AIGC时代!AI的“iPhone时刻”与投资机遇 前言AI的“iPhone时刻”与投资机遇 前言 AIGC,也就是人工智能生成内容,它就像是一股汹涌的浪潮,席卷了整个科技世界。它的出现,让我们看到了人工智能的无限潜力,也…...

Kubernetes调度单位Pod
Kubernetes调度单位Pod 1 Pod简介 不直接操作容器container。 一个 pod 可包含一或多个容器(container),它们共享一个 namespace(用户,网络,存储等),其中进程之间通过 localhost 本地…...

C语言指针篇
要想学好C语言,作为灵魂的指针那是必须要掌握的,而要想搞定指针,就不得不讲一下内存和地址之间的关系 内存和地址 计算机上的CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处…...

Unity 使用Editor工具查找 Prefab 中的指定脚本
在 Unity 项目中,随着项目规模的扩大和 Prefab 数量的增加,管理和定位 Prefab 中的脚本变得更加复杂。为了提高开发效率,所以需要编写一个自定义的 Unity Editor 工具,帮助查找某个 Prefab 中是否使用了指定的脚本。本文将介绍如何…...

Frida-JSAPI:Interceptor使用
拦截器 Interceptor.attach(target, callbacks[, data]) 参数分析 target :target是一个NativePointer,用于指定想要拦截的函数的地址。callbacks :参数是一个包含一个或多个回调函数的对象。 onEnter(args) 回调函数,接收一个参…...

【深度学习】(3)--损失函数
文章目录 损失函数一、L1Loss损失函数1. 定义2. 优缺点3. 应用 二、NLLLoss损失函数1. 定义与原理2. 优点与注意3. 应用 三、MSELoss损失函数1. 定义与原理2. 优点与注意3. 应用 四、BCELoss损失函数1. 定义与原理2. 优点与注意3. 应用 五、CrossEntropyLoss损失函数1. 定义与原…...

git学习报告
文章目录 git学习报告如何配置vscode终端安装PowerShell安装 Microsoft.Powershell.Preview使用 git的使用关于团队合作 git指令本地命令:云端指令 git学习报告 如何配置vscode 安装powershell调教window终端,使其像Linux一样,通过Linux命令…...

Spring MVC的应用
目录 1、创建项目与maven坐标配置 2、核心配置 3、启动项目测试 4、不同请求参数在controller的配置 4.1 servlet API 4.2 简单类型 4.3 pojo类型 4.4 日期类型 4.5 restful风格4种操作类型 4.5.1 GET:获取资源 4.5.2 POST:新建资源 4.5.3 P…...

JavaEE: 深入探索TCP网络编程的奇妙世界(六)
文章目录 TCP核心机制TCP核心机制九: 面向字节流TCP核心机制十: 异常处理 小小的补充(URG 和 PSH)~TCP小结TCP/UDP 对比用UDP实现可靠传输(经典面试题) 结尾 TCP核心机制 上一篇文章JavaEE: 深入探索TCP网络编程的奇妙世界(五) 书接上文~ TCP核心机制九: 面向字节流 TCP是面…...

探秘 Web Bluetooth API:连接蓝牙设备的新利器
引言 随着物联网技术的快速发展,蓝牙设备在日常生活中扮演着越来越重要的角色。而在 Web 开发领域,Web Bluetooth API 的出现为我们提供了一种全新的方式来连接和控制蓝牙设备。本文将深入探讨 Web Bluetooth API 的使用方法和原理,帮助开发…...