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

C++初阶(三)---C++入门(下)

目录

一、内联函数

1.内联函数的定义与底层机制

0x01.内联函数的定义

 0x02.内联函数的底层机制

2.内联函数的优缺点

优点:

缺点:

3.内联函数的使用建议

4.内联函数的注意事项

二、auto关键字(C++11)

1.代码示例

2.auto使用场景

0x01.迭代器遍历

0x02. Lambda表达式

0x03.范围for循环

0x04.复杂的类型推导

0x05.与decltype结合使用

 3.使用auto关键字注意事项

 0x01. 初始化是必需的

 0x02.auto 不能作为函数的参数 

 0x03.auto 不能直接用来声明数组 

 0x04.避免过度使用

 0x05.注意const和volatile的保留

 0x06.小心引用和指针的推导

 0x07.初始化表达式中的类型转换

 0x08.与模板结合使用时的小心

 4.auto关键字总结

三、基于范围的for循环(C++11)

1.范围for的语法

 2. 范围for的使用条件

 3.底层实现原理

 4.细节和注意事项

 5.范围for总结

 四、指针空值 nullptr(C++11)

1.nullptr的引入背景

2.nullptr与NULL的区别

3.nullptr的使用场景

0x01.初始化指针

0x02.比较指针

0x03.函数参数和返回值

0x05.避免类型混淆

 4.注意事项

 5.总结


一、内联函数

在C++编程中,内联函数(Inline Function)是一种强大的优化手段,它通过减少函数调用的开销来提高程序的执行效率。这里将深入探讨C++内联函数的底层机制、优缺点以及最佳实践,并插入一些示例代码以帮助你更好地理解。

1.内联函数的定义与底层机制

内联函数是C++语言中的一种特性,它允许程序员请求编译器将函数的定义(实现)直接插入到所有调用该函数的地方,而不是创建一个新的函数调用栈帧。这意味着当函数被调用时,它的代码会直接嵌入到调用处,而不是跳转到函数定义处执行。

0x01.内联函数的定义

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。

正常函数调用

内联函数调用

 从这里可以看到内联函数并没有建立栈帧,减少建立栈帧的开销从而提高程序的效率。

 0x02.内联函数的底层机制

  1. 内联请求与编译器决策

    • 当程序员使用inline关键字声明一个函数时,他们实际上是在向编译器提出一个内联请求
    • 编译器会根据函数的复杂性、大小、调用频率以及编译器的优化策略来决定是否接受这个请求。
    • 如果编译器决定不接受内联请求,该函数将被视为普通函数进行处理。
  2. 内联函数的展开

    • 当编译器接受内联请求时,它会在每个调用该函数的地方插入该函数的代码。
    • 这意味着每次函数调用都会被替换为函数体的实际代码。
    • 内联函数的展开是在编译时进行的,而不是在运行时
  3. 内联函数的局限性

    • 内联函数不能包含复杂的控制结构(如循环和递归),否则编译器可能会拒绝内联请求。
    • 内联函数通常用于小型、频繁调用的函数,以避免代码膨胀和编译时间增加。

2.内联函数的优缺点

优点

  1. 提高执行效率:内联函数避免了函数调用的开销,包括堆栈帧分配、参数传递和返回操作。
  2. 改善代码可读性:对于小型、简单的函数,内联函数可以使代码更易于阅读和理解。

缺点

  1. 代码膨胀:内联函数会导致调用处的代码膨胀,因为每次调用都会插入函数体的代码。
  2. 编译时间增加:编译器需要处理更多的代码,这可能会增加编译时间。
  3. 调试困难:由于代码被展开到调用点,调试时可能难以跟踪到原始的函数定义。

3.内联函数的使用建议

  1. 选择小型函数:仅内联小型函数,因为大型函数会产生代码膨胀,影响程序的执行效率。
  2. 避免递归函数:内联递归函数会导致堆栈溢出或编译器拒绝内联。
  3. 使用inline关键字:虽然inline只是一个建议,但使用它可以增加编译器内联函数的可能性。
  4. 剖析代码:使用剖析工具来识别哪些函数受益于内联。
  5. 函数定义放在头文件中:为了确保编译器在编译每个调用该函数的源文件时都能看到函数定义,通常将内联函数的定义放在头文件中。否则内联函数会认为在调用的地方展开,导致不生成地址。

4.内联函数的注意事项

  1. inline关键字只是建议inline关键字只是向编译器提出内联请求的建议,编译器可以选择是否接受这个请求。
  2. 避免过度使用:过度使用内联函数可能会导致代码膨胀和编译时间增加,从而降低程序的性能。
  3. 考虑代码的可移植性:在某些情况下,内联函数可能会导致代码在不同编译器或平台上的行为不一致。因此,在编写跨平台代码时,应谨慎使用内联函数。

二、auto关键字(C++11)

在C++11标准中,auto关键字被赋予了新的使命——类型推导。在此之前,C++中的auto主要用于声明变量的存储期为自动,即局部变量。然而,从C++11开始,auto关键字主要用于根据变量的初始化表达式自动推导其类型。

1.代码示例

int main()
{auto i = 42; // i的类型将被推断为int  auto ch = 'a';// ch的类型将被推断为charauto d = 3.14; // y的类型将被推断为double  auto z = "hello"; // z的类型将被推断为const char*cout << typeid(i).name() << endl;   // icout << typeid(ch).name() << endl;  // ccout << typeid(d).name() << endl;   // dcout << typeid(z).name() << endl;  // Preturn 0;
}

运行结果如下 :

 

2.auto使用场景

 0x01.迭代器遍历

在使用STL容器(如std::vectorstd::list等)时,auto可以自动推导迭代器的类型,避免显式指定迭代器类型的繁琐。

#include <iostream>  
#include <vector>  int main() 
{  std::vector<int> vec = {1, 2, 3, 4, 5};  for (auto it = vec.begin(); it != vec.end(); ++it) {  std::cout << *it << " ";  }  std::cout << std::endl;  return 0;  
}

0x02. Lambda表达式

#include <iostream>  
#include <algorithm>  
#include <vector>  int main() 
{  std::vector<int> vec = {1, 2, 3, 4, 5};  auto print = [](int x) { std::cout << x << " "; };  std::for_each(vec.begin(), vec.end(), print);  std::cout << std::endl;  return 0;  
}

0x03.范围for循环

范围for循环是C++11引入的一种简化数组或容器遍历的语法,auto可以自动推导集合元素的类型。

#include <iostream>  
#include <vector>  int main() 
{  std::vector<int> vec = {1, 2, 3, 4, 5};  for (auto num : vec) {  std::cout << num << std::endl;  }  return 0;  
}

0x04.复杂的类型推导

在处理复杂类型(如模板实例化后的类型、类型别名等)时,auto可以简化类型声明。

#include <iostream>  
#include <map>  
#include <string>  int main() 
{  std::map<std::string, int> myMap = {{"apple", 1}, {"banana", 2}};  for (const auto& pair : myMap) {  std::cout << pair.first << ": " << pair.second << std::endl;  }  // 使用auto简化类型推导,避免显式指定std::pair<const std::string, int>  auto it = myMap.find("apple");  if (it != myMap.end()) {  std::cout << "Found apple with value: " << it->second << std::endl;  }  return 0;  
}

 0x05.与decltype结合使用

在某些情况下,你可能需要保留变量的constvolatile属性,或者需要推导出一个与现有变量相同类型的新变量。这时可以使用decltypeauto结合。

#include <iostream>  int main() 
{  int x = 10;  decltype(auto) y = x; // y的类型是int,与x相同  const int z = 20;  decltype(auto) w = z; // w的类型是const int,保留了z的const属性  std::cout << "y: " << y << ", w: " << w << std::endl;  // 注意:不能修改w的值,因为它是const int类型  // w = 30; // 错误:不能给常量赋值  return 0;  
}

 3.使用auto关键字注意事项

 0x01. 初始化是必需的

auto变量必须在使用前进行初始化,因为auto是通过初始化表达式来推导类型的。未初始化的auto变量将导致编译错误。

auto x; // 错误:auto变量必须初始化  
auto y = 10; // 正确:y的类型被推导为int

0x02.auto 不能作为函数的参数 

#include<iostream>
using namespace std;
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

0x03.auto 不能直接用来声明数组 

#include<iostream>
using namespace std;
int main()
{int a[] = { 1,2,3 };auto b[] = { 4,5,6 };return 0;
}

0x04.避免过度使用

虽然auto可以简化代码,但过度使用可能会降低代码的可读性。特别是在类型信息对于理解代码逻辑很重要时,显式指定类型可能更清晰。

// 不推荐:过度使用auto可能导致代码难以阅读  
auto a = 5;  
auto b = "Hello";  
auto c = std::make_pair(a, b);  // 推荐:在类型明确且重要时,显式指定类型  
int a = 5;  
std::string b = "Hello";  
std::pair<int, std::string> c = std::make_pair(a, b);

 0x05.注意constvolatile的保留

auto在推导类型时不会保留顶层constvolatile限定符。如果你需要保留这些限定符,可以使用decltype(auto)

const int x = 10;  
auto y = x; // y的类型是int,丢失了const限定符  
decltype(auto) z = x; // z的类型是const int,保留了const限定符

 0x06.小心引用和指针的推导

当使用auto推导引用或指针类型时,要确保初始化表达式也是引用或指针,以避免意外的类型推导。

int x = 10;  
int& ref = x;  
auto a = ref; // a的类型是int,不是int&  
auto& b = ref; // b的类型是int&,正确推导为引用  int* ptr = &x;  
auto c = ptr; // c的类型是int*,不是int**  
auto* d = ptr; // d的类型是int*,正确推导为指针

 0x07.初始化表达式中的类型转换

如果初始化表达式涉及类型转换,auto将推导转换后的类型。

double d = 3.14;  
auto e = static_cast<int>(d); // e的类型是int,因为进行了静态类型转换

0x08.与模板结合使用时的小心

在模板编程中,auto可以用于类型推导,但要小心模板参数的类型推导和auto的交互。

template<typename T>  
void func(T x) 
{  auto y = x; // y的类型与x相同  
}  int main() 
{  func(42); // T被推导为int,y的类型也是int  func(3.14); // T被推导为double,y的类型也是double  
}

 4.auto关键字总结

auto关键字在C++中主要用于类型推导,特别是在处理STL容器、Lambda表达式、范围for循环以及复杂类型时,auto可以大大简化代码的编写。然而,过度使用auto可能会降低代码的可读性,特别是在类型信息对于理解代码逻辑至关重要时。因此,在使用auto时,需要权衡代码的简洁性和可读性。

三、基于范围的for循环(C++11)

在C++11中,范围for循环(Range-based for loop)是一项重要的新特性,它提供了一种简洁且高效的方法来遍历容器或数组中的元素。下面将深入探讨这一特性,通过代码和图片详细解释其工作原理和底层实现。

1.范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行

void TestFor()
{int array[] = { 1, 2, 3, 4, 5 };for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){array[i] *= 2;cout << array[i] << " ";}cout<<endl;for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p){cout << *p <<" ";}cout << endl;
}int main()
{TestFor();return 0;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因 此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范 围内用于迭代的变量,第二部分则表示被迭代的范围。

void TestFor()
{int array[] = { 1, 2, 3, 4, 5 };for (auto& e : array)e *= 2;for (auto e : array)cout << e << " ";
}int main()
{TestFor();return 0;
}

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。 

 2. 范围for的使用条件

对于数组而言,就是数组中第一个元素和最后一个元素的范围;

对于类而言,应该提供 begin和end的方法,begin和end就是for循环迭代的范围。

注意:以下代码就有问题,因为for的范围不确定。

void TestFor(int array[]){for(auto& e : array)cout<< e <<endl;}

这里传递过来的是数组的首元素地址,并不是数组,它会不知道范围是多少,所以会 报错

不仅如此,并且迭代的对象要实现 ++ 和 == 的操作。(关于迭代器这个问题,以后会讲,现在提一下,没办法讲清楚,现在uu了解一下就可以了)

 3.底层实现原理

// 原始的范围for循环  
for (int x : vec) 
{  std::cout << x << std::endl;  
}  // 编译器可能生成的等价代码  
auto it = vec.begin();  
auto end = vec.end();  
while (it != end) 
{  int x = *it;  std::cout << x << std::endl;  ++it;  
}

 4.细节和注意事项

  • 类型推导:范围for循环中的变量类型(如上面的int x)是通过类型推导(type deduction)来确定的。编译器会根据容器或数组中的元素类型来推导变量的类型。

  • 常量性:如果希望遍历过程中不修改容器中的元素,可以将变量声明为常量引用类型,例如const auto& x : vec

  • 范围for循环的限制:范围for循环不能用于修改容器的大小(如添加或删除元素),因为它不提供对迭代器的直接访问。如果需要修改容器,应使用传统的基于迭代器的循环。

  • 数组和容器的兼容性:范围for循环不仅适用于标准容器(如std::vectorstd::list等),还适用于原生数组和C风格的数组。

  • 性能考虑:对于简单的容器和数组,范围for循环的性能与基于迭代器的循环相当。然而,在某些复杂情况下(如需要频繁修改迭代器或进行复杂的条件判断),手动管理迭代器可能会提供更精细的控制和可能的性能优化。

5.范围for总结

总的来说,范围for循环是C++11引入的一个非常有用的特性,它简化了容器和数组的遍历操作,并提高了代码的可读性和可维护性。尽管它在底层是通过编译器转换为基于迭代器的循环来实现的,但这一转换对用户来说是透明的,使得用户能够专注于更高层次的逻辑和算法实现。

 四、指针空值 nullptr(C++11)

在C++编程中,指针是一个非常重要的概念,它允许我们直接操作内存地址。然而,指针的使用也伴随着一定的风险,特别是当指针未初始化或指向无效内存时。为了解决这个问题,C++11引入了一个新的关键字nullptr,以替代传统的空指针常量NULL或整数0。接下来将深入探讨nullptr的底层机制、使用场景以及它如何帮助提高代码的安全性和可读性。

1.nullptr的引入背景

在C++11之前,我们通常使用NULL0来表示空指针。然而,这两种方式都存在一些问题:

  • NULL通常被定义为((void*)0),这是一个类型转换表达式,而不是一个真正的关键字。这可能导致在某些上下文中出现类型不匹配的问题。
  • 使用0作为空指针常量虽然简单,但它与整数字面量0没有区别,这可能导致类型混淆和潜在的错误。

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL#ifdef __cplusplus#define NULL    0#else#define NULL    ((void *)0)#endif#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何 种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

void f(int){cout<<"f(int)"<<endl;}void f(int*){cout<<"f(int*)"<<endl;}int main(){f(0);f(NULL);f((int*)NULL);return 0;}

该程序的本意是想通过 Func(NULL) 调用指针版本的 Func(int*) 函数,

但是由于 NULL 被定义成0,这么一来就不符合程序的初衷了。

在 C++98 中,字面常量 0 既可以是一个整型数字,也可以是无类型的指针 (void*) 常量,

但是编译器默认情况下会将其看成一个整型常量,

如果要将其按照指针方式来使用,必须对其进行强制类型转换 (void*)0 。

为了解决这些问题,C++11引入了nullptr,它是一个类型安全的空指针常量。

2.nullptrNULL的区别

  • NULL是一个宏定义,而不是关键字。它在C++中通常被定义为0((void*)0),这可能导致类型不匹配的问题。
  • nullptr是一个关键字,它的类型是nullptr_t,是一个专门用于表示空指针的类型,与传统的空指针常量NULL(通常被定义为((void*)0)0)不同,nullptr只能被隐式转换为指针类型,而不能被转换为整数或其他非指针类型。这种类型安全性有助于减少类型混淆和潜在的错误。具有类型安全性。

3.nullptr的使用场景

0x01.初始化指针


使用nullptr来初始化指针是一个好习惯,它可以帮助我们避免未初始化指针导致的未定义行为。

int* ptr = nullptr;

0x02.比较指针


使用nullptr来比较指针是否为空,可以提高代码的可读性和安全性。

if (ptr == nullptr) 
{  // ptr is null  
}

0x03.函数参数和返回值


在函数参数和返回值中使用nullptr可以明确表示一个指针参数或返回值是可选的或可以为空。

void* allocateMemory(size_t size) 
{  if (size == 0) {  return nullptr;  }  // Allocate memory and return pointer  
}

0x05.避免类型混淆


使用nullptr可以避免将整数0误用作指针,从而减少类型混淆和潜在的错误。 

int* ptr = nullptr; // Clear and safe  
// int* ptr = 0; // Less clear and potentially error-prone

 4.注意事项

  • 避免类型混淆:不要将nullptr与整数0混用,以避免类型混淆和潜在的错误。
  • 初始化指针:始终在声明指针时初始化它,即使你暂时不知道它将指向什么。使用nullptr是一个好选择。
  • 检查空指针:在删除指针或访问指针所指向的内存之前,始终检查指针是否为空。
  • 避免悬挂指针:在删除动态分配的内存后,将指针设置为nullptr以避免悬挂指针的问题。
  • 模板中的使用:当将nullptr应用于模板时,模板会将其作为一个普通的类型来进行推导,并不会将其视为T*指针。因此,在模板函数中处理空指针时需要注意这一点。

5.总结

nullptr是C++11引入的一个非常重要的特性,它提高了指针操作的安全性和可读性。通过使用nullptr,我们可以避免类型混淆、减少潜在的错误,并编写更清晰、更健壮的代码。因此,在C++11及更高版本中,我们应该优先使用nullptr来替代传统的空指针常量NULL或整数0



 本篇博客到此结束,如有错误之处,望各位指正~

相关文章:

C++初阶(三)---C++入门(下)

目录 一、内联函数 1.内联函数的定义与底层机制 0x01.内联函数的定义 0x02.内联函数的底层机制 2.内联函数的优缺点 优点&#xff1a; 缺点&#xff1a; 3.内联函数的使用建议 4.内联函数的注意事项 二、auto关键字&#xff08;C11&#xff09; 1.代码示例 2.auto使…...

Linux--多路转接之epoll

上一篇:Linux–多路转接之select epoll epoll 是 Linux 下多路复用 I/O 接口 select/poll 的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。它是 Linux 下多路复用 API 的一个选择&#xff0c;相比 select 和 poll&#xff0c…...

自动化工具Nico,从零开始干掉Appium,移动端自动化测试框架实现

这篇将用较短的篇幅给大家介绍我是如何实现iOS和Android的inspector&#xff08;元素审查工具&#xff09;的。 实现原理 为了更方便的显示UI界面&#xff0c;且更容易制作&#xff0c;我选择了使用web端来承载整个元素树展示。同时我选用Flask一次性梭哈前后端&#xff08;因…...

Fast CRC32

链接&#xff1a; Fast CRC32 Error Checking Real life data tends to get corrupted because machines (and humans) are never as reliable as we wish for. One efficient way is make sure your data wasnt unintendedly modifiied is to generate some kind of hash. T…...

生成一个带有二维数据和对应标签的螺旋形数据集(非线性可分数据集)的代码解析

def create_dataset():np.random.seed(1)m 400 # 数据量N int(m/2) # 每个标签的实例数D 2 # 数据维度X np.zeros((m,D)) # 数据矩阵Y np.zeros((m,1), dtypeuint8) # 标签维度a 4 for j in range(2):ix range(N*j,N*(j1))t np.linspace(j*3.12,(j1)*3.12,N) np.rando…...

PHP unset() 函数的作用

PHP 中的 unset() 函数用于销毁指定的变量。具体来说&#xff0c;它会解除变量名与其数据之间的关联&#xff0c;从而释放该变量所占用的内存。不过需要注意的是&#xff0c;unset() 并不是删除变量的内容&#xff0c;而是取消对变量名的引用。如果变量是数组中的某个元素或者对…...

长篇故事可视化方法Story-Adapter:能够生成更高质量、更具细腻交互的故事图像,确保每一帧都能准确地传达故事情节。

今天给大家介绍一个最新的长篇故事可视化方法Story-Adapter&#xff0c;它的工作原理可以想象成一个画家在创作一幅长画卷。首先&#xff0c;画家根据故事的文本提示画出初步的图像。这些图像就像是画卷的草图。接下来&#xff0c;画家会不断回顾这些草图&#xff0c;逐步添加细…...

C++基础面试题 | 什么是C++中的运算符重载?

文章目录 回答重点&#xff1a;示例&#xff1a; 运算符重载的基本规则和注意事项&#xff1a; 回答重点&#xff1a; C的运算符重载是指可以为自定义类型&#xff08;如类或结构体&#xff09;定义运算符的行为&#xff0c;使其像内置类型一样使用运算符。通过重载运算符&…...

深入 IDEA 字节码世界:如何轻松查看 .class 文件?

前言&#xff1a; 作为一名 Java 开发者&#xff0c;理解字节码对于优化程序性能、调试错误以及深入了解 JVM 运行机制非常重要。IntelliJ IDEA 作为最流行的开发工具之一&#xff0c;为开发者提供了查看 .class 文件字节码的功能。在本文中&#xff0c;我将带你一步步探索如何…...

NodeJS 利用代码生成工具编写GRPC

生成的 gRPC 代码优点 自动化和效率: 减少手动编码:生成代码自动处理了消息的序列化和反序列化、服务接口的定义等,减少了手动编码的工作量。一致性:生成的代码确保了客户端和服务器之间的一致性,避免了手动编码可能带来的错误。跨语言支持: 多语言兼容:gRPC 支持多种编…...

uni-app基础语法(一)

我们今天的学习目标 基础语法1. 创建新页面2.pages配置页面3.tabbar配置4.condition 启动模式配置 基础语法 1. 创建新页面 2.pages配置页面 属性类型默认值描述pathString配置页面路径styleObject配置页面窗口表现&#xff0c;配置项参考pageStyle 我们来通过style修改页面的…...

Linux:进程控制(三)——进程程序替换

目录 一、概念 二、使用 1.单进程程序替换 2.多进程程序替换 3.exec接口 4.execle 一、概念 背景 当前进程在运行的时候&#xff0c;所执行的代码来自于自己的源文件。使用fork创建子进程后&#xff0c;子进程执行的程序中代码内容和父进程是相同的&#xff0c;如果子进…...

LeetCode279:完全平方数

题目链接&#xff1a;279. 完全平方数 - 力扣&#xff08;LeetCode&#xff09; 代码如下 class Solution { public:int numSquares(int n) {vector<int> dp(n 1, INT_MAX);dp[0] 0;for(int i 1; i * i < n; i){for(int j i * i; j < n; j){dp[j] min(dp[j …...

python爬虫--某动漫信息采集

python爬虫--tx动漫 一、采集主页信息二、采集详情页信息三、代码供参考一、采集主页信息 略。 二、采集详情页信息 如上图所示,使用xpath提取详情页的标题、作者、评分、人气、评论人数等数据。 三、代码供参考 import csv import time import random import requests fr…...

使用Rollup.js快速开始构建一个前端项目

Rollup 是一个用于 JavaScript 项目的模块打包器&#xff0c;它将小块代码编译成更大、更复杂的代码&#xff0c;例如库或应用程序。Rollup 对代码模块使用 ES6 模块标准&#xff0c;它支持 Tree-shaking&#xff08;摇树优化&#xff09;&#xff0c;可以剔除那些实际上没有被…...

10.15学习

1.程序开发的步骤 定义程序的目标→设计程序→编写代码&#xff08;需要选择语言&#xff0c;一种语言对应一种编译器&#xff09;→编译→运行程序→测试和调试程序→维护和修改程序 2.ANSI/ISO C标准 1989年ANSI批准通过&#xff0c;1990年ISO批准通过&#xff0c;因此被称…...

mongodb-7.0.14分片副本集超详细部署

mongodb介绍&#xff1a; 是最常用的nosql数据库&#xff0c;在数据库排名中已经上升到了前六。这篇文章介绍如何搭建高可用的mongodb&#xff08;分片副本&#xff09;集群。 环境准备 系统系统 BC 21.10 三台服务器&#xff1a;192.168.123.247/248/249 安装包&#xff1a…...

C++运算出现整型溢出

考虑如下代码&#xff1a; int aINT_MAX; int b 1; long c ab; 这段代码没有编过&#xff01; 原因是a和b都是int型&#xff0c;相加之后会溢出。 请记住&#xff0c;c语言没有赋值&#xff0c;只有表达式&#xff0c;右侧会存在一个暂存的int保存ab的值&#xff0c;而明…...

LeetCode岛屿数量

题目描述 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该网…...

Karmada核心概念

以下内容为翻译&#xff0c;原文地址 Karmada 是什么&#xff1f; | karmada 一、Karmada核心概念 一&#xff09;什么是Karmada 1、Karmada&#xff1a;开放&#xff0c;多云&#xff0c;多集群Kubernetes业务流程 Karmada (Kubernetes Armada)是一个Kubernetes管理系统&…...

Rust 与生成式 AI:从语言选择到开发工具的演进

在现代软件开发领域&#xff0c;Rust 语言正在逐步崭露头角&#xff0c;尤其是在高性能和可靠性要求较高的应用场景。与此同时&#xff0c;生成式 AI 的崛起正在重新塑造开发者的工作方式&#xff0c;从代码生成到智能调试&#xff0c;生成式 AI 的应用正成为提升开发效率和质量…...

Python爬虫高效数据爬取方法

大家好!今天我们来聊聊Python爬虫中那些既简洁又高效的数据爬取方法。作为一名爬虫工程师,我们总是希望用最少的代码完成最多的工作。下面我ll分享一些在使用requests库进行网络爬虫时常用且高效的函数和方法。 1. requests.get() - 简单而强大 requests.get()是我们最常用的…...

C语言之扫雷小游戏(完整代码版)

说起扫雷游戏&#xff0c;这应该是很多人童年的回忆吧&#xff0c;中小学电脑课最常玩的必有扫雷游戏&#xff0c;那么大家知道它是如何开发出来的吗&#xff0c;扫雷游戏背后的原理是什么呢&#xff1f;今天就让我们一探究竟&#xff01; 扫雷游戏介绍 如下图&#xff0c;简…...

Spring WebFlux 响应式概述(1)

1、响应式编程概述 1.1、响应式编程介绍 1.1.1、为什么需要响应式 传统的命令式编程在面对当前的需求时的一些限制。在应用负载较高时&#xff0c;要求应用需要有更高的可用性&#xff0c;并提供低的延迟时间。 1、Thread per Request 模型 比如使用Servlet开发的单体应用&a…...

Unity游戏通用框架——事件的订阅和发布(观察者模式)

在游戏开发的基本思想中&#xff0c;逻辑与表现的分离极为重要&#xff0c;相互之间并不关心具体实现&#xff0c;只注册对应的事件&#xff0c;有事件发生时才调用相应的函数 事件管理器 using System.Collections; using System.Collections.Generic;public class event_ma…...

将 Ubuntu 系统中的 **swap** 空间从 2GB 扩展到 16GB

要将 Ubuntu 系统中的 swap 空间从 2GB 扩展到 16GB&#xff0c;可以按照以下步骤操作&#xff1a; 1. 关闭现有 Swap 文件 首先需要禁用当前的 swap 文件&#xff0c;以便重新调整其大小。 sudo swapoff -a2. 删除旧的 Swap 文件 假设当前的 swap 文件位于 /swapfile&…...

流程图 LogicFlow

流程图 LogicFlow 官方文档&#xff1a;https://site.logic-flow.cn/tutorial/get-started <script setup> import { onMounted, ref } from vue import { forEach, map, has } from lodash-es import LogicFlow, { ElementState, LogicFlowUtil } from logicflow/core …...

Mac通过键盘选取内容

问题&#xff1a; 我们在使用键盘的时候经常懒得动手去拿鼠标了&#xff0c;并且熟练使用键盘可以提高我们的工作效率&#xff0c;比如在我们需要复制内容的时候&#xff0c;可以仅仅通过键盘来选取想要的内容&#xff1b; 解决&#xff1a; 将鼠标光标移动到想要选取的内容…...

如何通过OpenCV实现图像融合拼接?

图像拼接的意义 2024年了&#xff0c;谈论图像拼接&#xff0c;不算新事物&#xff0c;我们这里探讨图像拼接&#xff0c;主要探讨图像拼接的意义、难点和大概的实现思路。图像拼接可以突破设备视野限制&#xff0c;通过拼接低分辨率图像获得高分辨率图像。 扩展视野&#xff…...

Qt5.14.2 安装详细教程(图文版)

Qt 是一个跨平台的 C 应用程序开发框架&#xff0c;主要用于开发图形用户界面&#xff08;GUI&#xff09;程序&#xff0c;但也支持非 GUI 程序的开发。Qt 提供了丰富的功能库和工具&#xff0c;使开发者能够在不同平台上编写、编译和运行应用程序&#xff0c;而无需修改代码。…...