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

C++11新特性(列表初始化与右值引用折叠与完美转发)

c++11

  • 列表初始化
    • c++98的{}
    • c++11的{}
    • std::initializer_list
  • 右值引用和移动语义
    • 左值和右值的概念
    • 左值引用和右值引用
    • 引用延长临时对象生命周期
    • 左值和右值的参数匹配
    • 左值引用的主要使用场景
    • 移动构造和移动赋值
    • 引用折叠
    • 完美转发

列表初始化

c++98的{}

在C++98中,大括号 {} 的使用主要有以下几种场景:

  1. 代码块
    大括号用于定义一个代码块,通常在控制结构(如 if、for、while 等)或函数定义中使用。
if (condition) {// 代码块doSomething();
}
  1. 函数定义
    函数体由大括号括起来。
void myFunction() {// 函数体
}
  1. 类和结构体定义
    类和结构体的成员变量和成员函数也由大括号括起来。
class MyClass {
public:int x;void myMethod() {// 方法体}
};
  1. 初始化列表(C++98 限制)
    在C++98中,可以使用大括号进行数组或结构体的初始化。
int arr[5] = {1, 2, 3, 4, 5};struct Point {int x;int y;
};Point p = {10, 20};
  1. 范围(Scope)管理
    大括号可以创建新的作用域,这对于管理变量的生命周期很有用。
{int temp = 5;// temp 在这个作用域内有效
}
// temp 在这里不可见
  1. C++98 对 {} 的局限
    C++98 对大括号的支持相对有限,没有像 C++11 引入的统一初始化(Uniform Initialization)那样的功能。

c++11的{}

在 C++11 中,大括号 {} 的使用引入了一些重要的变化和新特性,尤其是在初始化和类型安全方面。以下是主要变化:

  1. 统一初始化(Uniform Initialization)
    C++11 引入了统一初始化语法,可以使用大括号来初始化各种类型的变量,包括基本类型、结构体、类和数组。这种语法帮助避免了一些常见的错误。
int x{5};              // 初始化基本类型
std::vector<int> vec{1, 2, 3};  // 初始化容器
struct Point { int x, y; };
Point p{10, 20};      // 初始化结构体
  1. 列表初始化(List Initialization)
    使用大括号进行列表初始化可以避免窄化转换(narrowing conversion)。例如:
// 这将会导致编译错误,避免窄化转换
int x{2.5};  // 错误:不能从 double 转换为 int
  1. 初始化数组
    在 C++11 中,数组的初始化也可以使用统一初始化语法:
int arr[]{1, 2, 3, 4, 5};  // C++11 引入的数组初始化
  1. nullptr
    虽然 nullptr 不是直接与 {} 相关,但它与统一初始化一起使用时,可以提供更好的类型安全:
int* ptr{nullptr};  // 安全的空指针初始化
  1. lambda 表达式
    在 C++11 中,引入了 lambda 表达式,虽然大括号在这里用作 lambda 的函数体,但这也是一种新的使用方式。
auto func = []() {std::cout << "Hello, World!" << std::endl;
};  // 大括号定义 lambda 的主体
  1. std::initializer_list
    C++11 引入了 std::initializer_list,允许使用大括号初始化自定义类型,方便处理多个值的初始化。
class MyClass {
public:MyClass(std::initializer_list<int> list) {for (auto& item : list) {std::cout << item << " ";}}
};// 使用大括号初始化
MyClass obj{1, 2, 3, 4};
  1. 案例
#include<iostream>
#include<vector>
using namespace std;
struct Point
{int _x;int _y;
};
class Date
{ public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;} Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}
private:int _year;int _month;int _day;
};
// ⼀切皆可⽤列表初始化,且可以不加=int main()
{// C++98⽀持的int a1[] = { 1, 2, 3, 4, 5 };int a2[5] = { 0 };Point p = { 1, 2 };// C++11⽀持的// 内置类型⽀持int x1 = { 2 };// ⾃定义类型⽀持// 这⾥本质是⽤{ 2025, 1, 1}构造⼀个Date临时对象// 临时对象再去拷⻉构造d1,编译器优化后合⼆为⼀变成{ 2025, 1, 1}直接构造初始化d1// 运⾏⼀下,我们可以验证上⾯的理论,发现是没调⽤拷⻉构造的Date d1 = { 2025, 1, 1};// 这⾥d2引⽤的是{ 2024, 7, 25 }构造的临时对象const Date& d2 = { 2024, 7, 25 };// 需要注意的是C++98⽀持单参数时类型转换,也可以不⽤{}Date d3 = { 2025};Date d4 = 2025;// 可以省略掉=Point p1 { 1, 2 };int x2 { 2 };Date d6 { 2024, 7, 25 };const Date& d7 { 2024, 7, 25 };// 不⽀持,只有{}初始化,才能省略=// Date d8 2025;vector<Date> v;v.push_back(d1);v.push_back(Date(2025, 1, 1));// ⽐起有名对象和匿名对象传参,这⾥{}更有性价⽐v.push_back({ 2025, 1, 1 });return 0;
}

std::initializer_list

std::initializer_list 是 C++11 中引入的一个非常有用的特性,允许你使用大括号 {} 进行初始化,特别适用于需要接收多个元素的构造函数、函数参数或其他需要集合的情况。

基本概念
std::initializer_list 是一个轻量级的类模板,提供了一个可迭代的常量数组,可以用来接收初始化的元素。

用法示例

  1. 构造函数
    你可以在类的构造函数中使用 std::initializer_list 来接收多个值。
#include <iostream>
#include <initializer_list>
#include <vector>class MyVector {
public:MyVector(std::initializer_list<int> list) {for (auto& value : list) {vec.push_back(value);}}void print() const {for (const auto& value : vec) {std::cout << value << " ";}std::cout << std::endl;}private:std::vector<int> vec;
};int main() 
{MyVector mv{1, 2, 3, 4, 5};  // 使用 initializer_listmv.print();                  // 输出: 1 2 3 4 5return 0;
}
  1. 函数参数
    std::initializer_list 也可以用作函数的参数,让函数能够接收多个输入。
void printNumbers(std::initializer_list<int> numbers) {for (auto& number : numbers) {std::cout << number << " ";}std::cout << std::endl;
}int main() {printNumbers({10, 20, 30});  // 使用 initializer_listreturn 0;
}
  1. 注意事项
    std::initializer_list 是常量类型,因此你不能修改它的内容。
    由于它是一个轻量级的类,可以在需要的地方使用,避免了额外的内存分配。

右值引用和移动语义

左值和右值的概念

左值和右值是 C++ 中的重要概念,用于区分表达式的值类型。它们在理解资源管理和对象生命周期时尤为关键。

左值 (Lvalue)

  • 定义:左值是指可以被赋值的对象,可以出现在赋值操作符的左侧。左值有持久的存储地址,可以在程序中多次访问。
  • 特点:
    有名字,并且在内存中有一个具体的位置。
    可以被修改,例如变量。
  • 示例:
int a = 10;  // 'a' 是一个左值
a = 20;      // 可以将值赋给左值 'a'

右值 (Rvalue)

  • 定义:右值是指不具名的临时对象,通常是表达式的结果,不能出现在赋值操作符的左侧。右值通常没有持久的存储地址。
  • 特点:
    通常是临时的、不可修改的值。
    可以是字面量、运算结果或函数返回值等。
  • 示例:
int a = 10;        // 10 是一个右值
a = a + 5;        // 'a + 5' 也是一个右值

结合与应用

在 C++11 中,引入了右值引用(&&),使得可以更有效地处理临时对象,优化移动语义,从而提高性能。简单来说左值就是可以取地址的,而右值不可以。

案例

#include<iostream>
using namespace std;
int main()
{// 左值:可以取地址// 以下的p、b、c、*p、s、s[0]就是常⻅的左值int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("111111");s[0] = 'x';cout << &c << endl;cout << (void*)&s[0] << endl;// 右值:不能取地址double x = 1.1, y = 2.2;// 以下⼏个10、x + y、fmin(x, y)、string("11111")都是常⻅的右值10;x + y;fmin(x, y);string("11111");//cout << &10 << endl;//cout << &(x+y) << endl;//cout << &(fmin(x, y)) << endl;//cout << &string("11111") << endl;return 0;
}

左值引用和右值引用

  • Type& r1 = x; Type&& rr1 = y; 第⼀个语句就是左值引⽤,左值引⽤就是给左值取别名,第⼆个就是右值引⽤,同样的道理,右值引⽤就是给右值取别名。

  • 左值引⽤不能直接引⽤右值,但是const左值引⽤可以引⽤右值

  • 右值引⽤不能直接引⽤左值,但是右值引⽤可以引⽤move(左值)

  • template typename remove_reference::type&& move (T&&arg);

  • move是库⾥⾯的⼀个函数模板,本质内部是进⾏强制类型转换,还涉及⼀些引⽤折叠的知识。

  • 需要注意的是变量表达式都是左值属性,也就意味着⼀个右值被右值引⽤绑定后,右值引⽤变量变量表达式的属性是左值。

下面来解释下这句话。

1, 变量表达式都是左值

这意味着所有的变量(如 int a = 10; 中的 a)都是左值,因为它们有一个持久的存储位置,可以被赋值和修改。

2,右值被右值引用绑定

右值引用是 C++11 引入的特性,表示可以绑定到右值的引用,通常用 && 表示。比如:

int&& r = 10; // 10 是一个右值

在这里,r 是一个右值引用,它可以绑定到临时值 10。

3, 右值引用变量的属性是左值

一旦一个右值被绑定到右值引用,虽然这个引用最初绑定的是一个右值,但 r 现在本身是一个可以在赋值中使用的对象。在这种情况下,右值引用 r 的性质变为左值,因为它现在有一个具体的名称和存储位置。

具体示例

int&& r = 10; // r 绑定到右值 10
int a = std::move(r); // std::move(r) 是一个右值,但 r 是左值

在上面的例子中,std::move(r ) 使得 r 的值能够被移动(即转移资源),但 r 本身是一个左值。

template <class _Ty>
remove_reference_t<_Ty>&& move(_Ty&& _Arg)
{ // forward _Arg as movablereturn static_cast<remove_reference_t<_Ty>&&>(_Arg);
}#include<iostream>
using namespace std;
int main()
{// 左值:可以取地址// 以下的p、b、c、*p、s、s[0]就是常⻅的左值int* p = new int(0);int b = 1;const int c = b;*p = 10;string s("111111");s[0] = 'x';double x = 1.1, y = 2.2;// 左值引⽤给左值取别名int& r1 = b;int*& r2 = p;int& r3 = *p;string& r4 = s;char& r5 = s[0];// 右值引⽤给右值取别名int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);string&& rr4 = string("11111");// 左值引⽤不能直接引⽤右值,但是const左值引⽤可以引⽤右值const int& rx1 = 10;const double& rx2 = x + y;const double& rx3 = fmin(x, y);const string& rx4 = string("11111");// 右值引⽤不能直接引⽤左值,但是右值引⽤可以引⽤move(左值)int&& rrx1 = move(b);int*&& rrx2 = move(p);int&& rrx3 = move(*p);string&& rrx4 = move(s);string&& rrx5 = (string&&)s;// b、r1、rr1都是变量表达式,都是左值cout << &b << endl;cout << &r1 << endl;cout << &rr1 << endl;// 这⾥要注意的是,rr1的属性是左值,所以不能再被右值引⽤绑定,除⾮move⼀下int& r6 = r1;// int&& rrx6 = rr1;int&& rrx6 = move(rr1);return 0;
}

引用延长临时对象生命周期

在这里插入图片描述

  1. 基本概念

在 C++ 中,当你创建一个临时对象(通常是一个右值),其生命周期通常非常短暂。在表达式结束后,临时对象会被销毁。但当这个临时对象被一个常量引用(const T&)或者右值引用绑定时,它的生命周期会被延长,直到该引用的作用域结束。

  1. 示例
const std::string& str = "Hello, World!"; // "Hello, World!" 是一个临时对象
std::cout << str; // 在这里可以使用 str,因为临时对象的生命周期被延长

在这个例子中,字符串字面量 “Hello, World!” 是一个临时对象,正常情况下会在语句结束后销毁。然而,由于它被一个 const std::string& 引用绑定,临时对象的生命周期被延长,直到 str 的作用域结束(例如,在函数返回时)。

  1. 适用场景

引用延长生命周期主要用于以下几种情况:

返回值优化:当函数返回一个临时对象时,可以通过返回一个常量引用来延长其生命周期。

避免不必要的复制:使用常量引用可以避免对临时对象的拷贝,提高性能。

  1. 注意事项

不可修改:使用常量引用只能读取临时对象的值,不能修改。

作用域管理:引用的作用域决定了临时对象的有效性,如果引用超出其作用域,临时对象会被销毁,使用引用将导致未定义行为。

左值和右值的参数匹配

在 C++ 中,函数可以接受不同类型的参数,包括左值引用和右值引用。以下是几种常见的参数类型及其匹配规则:

  • 左值引用(T&):只能绑定到左值。
  • 右值引用(T&&):只能绑定到右值。
  • 常量左值引用(const T&):可以绑定到左值和右值,因为常量左值引用允许对临时对象的绑定。
  • 常量右值引用(const T&&):理论上不常用,因为右值引用本身就是临时对象,通常不需要常量修饰。

案例

#include <iostream>
#include <utility> // for std::movevoid process(int& x) {std::cout << "Lvalue reference: " << x << std::endl;
}void process(int&& x) {std::cout << "Rvalue reference: " << x << std::endl;
}void process(const int& x) {std::cout << "Const lvalue reference: " << x << std::endl;
}int main() {int a = 10;process(a);           // 绑定到左值,调用 int& 版本process(20);         // 绑定到右值,调用 int&& 版本process(std::move(a)); // 绑定到右值,调用 int&& 版本process(30);         // 绑定到右值,调用 int&& 版本process(static_cast<const int&>(20)); // 绑定到常量左值引用return 0;
}

选择规则

  • 在函数重载时,C++ 使用以下规则来确定哪个版本的函数将被调用:

  • 精确匹配:如果有左值和右值的重载,C++ 会优先选择精确匹配的版本。

  • const 修饰符:如果没有左值引用匹配,const 左值引用会被考虑。

  • 临时对象:对于右值,右值引用会优先匹配。

左值引用的主要使用场景

左值引用在 C++ 中有许多重要的应用场景:

修改已有对象:左值引用可以用来修改函数外部的变量。例如,当你希望在函数中改变传入的参数值时,可以使用左值引用。

void increment(int& x) {x++;
}int main() {int a = 5;increment(a); // a 现在是 6
}

作为返回值:函数可以返回左值引用,从而允许链式调用。

int& getElement(std::vector<int>& vec, size_t index) {return vec[index];
}

避免复制:通过使用左值引用,可以避免对象的复制,提高性能,特别是在处理大对象时。

缺点
1,左值引用只能绑定到具有持久性地址的对象,不能绑定到临时对象。这意味着无法直接修改临时值或表达式的结果。

2,左值引用可以被赋值,但不能用于移动语义。这意味着使用左值引用的函数不能轻易地转移资源的所有权,从而可能导致不必要的资源开销。

移动构造和移动赋值

移动构造(Move Constructor)

概念
移动构造函数是一种特殊的构造函数,用于通过移动一个对象的资源来初始化另一个对象,而不是复制资源。这种方式适用于临时对象或即将被销毁的对象。

语法
移动构造函数的定义通常采用以下形式:

ClassName(ClassName&& other) noexcept {// 转移资源this->data = other.data;other.data = nullptr; // 使原对象的指针失效
}

示例

class MyString {
public:MyString(const char* str) {data = new char[strlen(str) + 1];strcpy(data, str);}// 移动构造函数MyString(MyString&& other) noexcept {data = other.data;other.data = nullptr; // 使其他对象的指针失效}~MyString() {delete[] data;}private:char* data;
};

移动赋值(Move Assignment Operator)

概念
移动赋值运算符是一种特殊的赋值运算符,用于将一个对象的资源转移到另一个对象,而不是复制资源。这在赋值操作中非常有用,尤其是在处理临时对象时。

语法
移动赋值运算符的定义通常采用以下形式:

ClassName& operator=(ClassName&& other) noexcept {if (this != &other) { // 防止自我赋值delete[] data; // 释放当前资源data = other.data; // 转移资源other.data = nullptr; // 使原对象的指针失效}return *this;
}

示例

class MyString {
public:MyString(const char* str) {data = new char[strlen(str) + 1];strcpy(data, str);}// 移动赋值运算符MyString& operator=(MyString&& other) noexcept {if (this != &other) {delete[] data; // 释放当前资源data = other.data; // 转移资源other.data = nullptr; // 使其他对象的指针失效}return *this;}~MyString() {delete[] data;}private:char* data;
};

意义

  • 性能提升:通过移动构造和移动赋值,C++ 可以避免不必要的深拷贝,显著提升性能,尤其是在处理大对象或复杂数据结构时。

  • 资源管理:移动语义提供了一种简洁且高效的方式来管理动态资源,降低内存分配和释放的频率。

  • 支持临时对象:移动构造和移动赋值使得临时对象的使用变得更加高效,避免了临时对象的拷贝。

引用折叠

有人可能会想到,能不能套娃引用的引用?确实可以,不过直接这样写是报错的,需要通过取巧的方式,这种情况就会引发引用折叠。

在这里插入图片描述

通过typedef的方式或者模板才能引用叠加,不过这种情况下,叠加的引用算什么引用呢?
在这里插入图片描述

引用叠加的情况

通过模板或typedef中的类型操作可以构成引⽤的引⽤时,这C++11给出了⼀个引⽤折叠的规则:右值引⽤的右值引⽤折叠成右值引⽤,所有其他组合均折叠成左值引⽤。

  • 下⾯的程序中很好的展⽰了模板和typedef时构成引⽤的引⽤时的引⽤折叠规则。

  • 像f2这样的函数模板中,T&& x参数看起来是右值引⽤参数,但是由于引⽤折叠的规则,他传递左值时就是左值引⽤,传递右值时就是右值引⽤,有些地⽅也把这种函数模板的参数叫做万能引⽤。

  • Function(T&&t)函数模板程序中,假设实参是int右值,模板参数T的推导int,实参是int左值,模板参数T的推导int&,再结合引⽤折叠规则,就实现了实参是左值,实例化出左值引⽤版本形参Function,实参是右值,实例化出右值引⽤版本形参的Function。

// 由于引⽤折叠限定,f1实例化以后总是⼀个左值引⽤
template<class T>
void f1(T& x)
{}
// 由于引⽤折叠限定,f2实例化后可以是左值引⽤,也可以是右值引⽤
template<class T>
void f2(T&& x)
{}
int main()
{typedef int& lref;typedef int&& rref;int n = 0;lref& r1 = n; // r1 的类型是 int&lref&& r2 = n; // r2 的类型是 int&rref& r3 = n; // r3 的类型是 int&rref&& r4 = 1; // r4 的类型是 int&&// 没有折叠->实例化为void f1(int& x)f1<int>(n);f1<int>(0); // 报错// 折叠->实例化为void f1(int& x)f1<int&>(n);f1<int&>(0); // 报错// 折叠->实例化为void f1(int& x)f1<int&&>(n);f1<int&&>(0); // 报错// 折叠->实例化为void f1(const int& x)f1<const int&>(n);f1<const int&>(0);// 折叠->实例化为void f1(const int& x)f1<const int&&>(n);f1<const int&&>(0);// 没有折叠->实例化为void f2(int&& x)f2<int>(n); // 报错f2<int>(0);// 折叠->实例化为void f2(int& x)f2<int&>(n);f2<int&>(0); // 报错// 折叠->实例化为void f2(int&& x)f2<int&&>(n); // 报错f2<int&&>(0);return 0;
}template<class T>
void Function(T&& t)
{int a = 0;T x = a;//x++;cout << &a << endl;cout << &x << endl << endl;
} 
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&&t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// a是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int&t)// 所以Function内部会编译报错,x不能++Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&& t)// 所以Function内部会编译报错,x不能++Function(std::move(b)); // const 右值return 0;
}

完美转发

Function(T&&t)函数模板程序中,传左值实例化以后是左值引⽤的Function函数,传右值实例化以后是右值引⽤的Function函数。

  • 变量表达式都是左值属性,也就意味着⼀个右值被右值绑定
    后,右值引⽤变量表达式的属性是左值
    ,也就是说Function函数中t的属性是左值,那么我们把t传递给下⼀层函数Fun,那么匹配的都是左值引⽤版本的Fun函数。这⾥我们想要保持t对象的属性,就需要使⽤完美转发实现。

  • 完美转发forward本质是⼀个函数模板,他主要还是通过引⽤折叠的⽅式实现,下⾯⽰例中传递给Function的实参是右值,T被推导为int,没有折叠,forward内部t被强转为右值引⽤返回;传递Function的实参是左值,T被推导为int&,引⽤折叠为左值引⽤,forward内部t被强转为左值引⽤返回。

template <class _Ty>
_Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept
{ // forward an lvalue as either an lvalue or an rvaluereturn static_cast<_Ty&&>(_Arg);
} 
void Fun(int& x) { cout << "左值引⽤" << endl; }void Fun(const int& x) { cout << "const 左值引⽤" << endl; }void Fun(int&& x) { cout << "右值引⽤" << endl; }void Fun(const int&& x) { cout << "const 右值引⽤" << endl; }template<class T>
void Function(T&& t)
{Fun(t);//Fun(forward<T>(t));
} 
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// a是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int&	t)Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&	t)Function(std::move(b)); // const 右值return 0;
}

相关文章:

C++11新特性(列表初始化与右值引用折叠与完美转发)

c11 列表初始化c98的{}c11的{}std::initializer_list 右值引用和移动语义左值和右值的概念左值引用和右值引用引用延长临时对象生命周期左值和右值的参数匹配左值引用的主要使用场景移动构造和移动赋值引用折叠完美转发 列表初始化 c98的{} 在C98中&#xff0c;大括号 {} 的使…...

基于SSH的物流运输货运车辆管理系统源码

基于经典的ssh&#xff08;Spring Spring MVC Hibernate&#xff09;框架与SaaS&#xff08;软件即服务&#xff09;模式&#xff0c;我们为运输企业与物流公司打造了一款开源且易用的车辆管理系统。 该系统主要包含以下核心模块&#xff1a; 档案管理 财务管理 借款管理 保…...

基于RabbitMQ,Redis,Redisson,RocketMQ四种技术实现订单延时关闭功能及其相关优缺点介绍(以12306为主题)

目录 1. 延迟关闭订单 1.1 订单延时关闭功能技术选型 1.1.1 定时任务 1.1.2 RabbitMQ 1.1.3 Redis 过期监听 1.1.4 Redisson 1.1.5 RocketMQ 1.2 RocketMQ订单延时关闭发送方实现 1.3 RocketMQ订单延时关闭的消费方实现 1. 延迟关闭订单 用户发起订单后&#xff0c;如…...

HarmonyOS ArkTS与C++数据类型转换

1. HarmonyOS ArkTS与C数据类型转换 本文介绍了C与TS各自数据类型与互相之间的数据类型转换&#xff0c;在需要使用C模块时可以快速上手对各种数据类型进行转换。 1.1. 概述 HarmonyOS的主力开发语言是ArkTS&#xff0c;也提供了C语言的支持&#xff0c;对于一些能力&#xff…...

腾讯云或阿里云centos7安装Redis,并解决端口无法访问的问题

问题背景 最近自建的网站JeecgFlow在云环境安装redis时候&#xff0c;出现端口无法远程进行访问。 浪费好了好久时间进行排查&#xff0c; 记录一下Redis在云环境centos7环境下如何安装&#xff0c;并且远程访问。 Redis安装 //安装c 用于编译redis yum install gcc-c//在/u…...

【小问题】距离估计和频率估计的方差下界推导出距离估计的方差下界

【1】OFDM Radar Algorithms in Mobile Communication Networks pp34 文章目录 1. 频率和距离之间的关系2. 计算 d ^ \hat{d} d^ 对 n ^ \hat{n} n^ 的导数3. 将频率的方差转化为距离的方差4. 从频率的 CRB 获得 var ⁡ [ n ^ ] \operatorname{var}[\hat{n}] var[n^]5. 将 …...

Selenium爬虫技术:如何模拟鼠标悬停抓取动态内容

介绍 在当今数据驱动的世界中&#xff0c;抓取动态网页内容变得越来越重要&#xff0c;尤其是像抖音这样的社交平台&#xff0c;动态加载的评论等内容需要通过特定的方式来获取。传统的静态爬虫方法难以处理这些由JavaScript生成的动态内容&#xff0c;Selenium爬虫技术则是一…...

Z-BlogPHP显示错误Undefined array key 0 (set_error_handler)的解决办法

今天打开博客的时候&#xff0c;意外发现页面&#xff0c;打开均显示错误&#xff1a;Undefined array key 0 (set_error_handler)。 博客程序采用的是Z-BlogPHP。百度了一圈没有找到解决办法&#xff0c;在官方论坛里也没找到解决办法。 于是开始自己排查原因。我服务器采用…...

java-实例化一个List,然后添加数据的方法详解

在Java中&#xff0c;实例化一个 List 并向其中添加数据非常简单。List 是一个接口&#xff0c;因此我们通常使用它的常见实现类 ArrayList 或 LinkedList。以下是一些常见的操作方法&#xff1a; ### 1. 使用 ArrayList 实例化并添加数据 java import java.util.ArrayList; …...

【Linux系统】Ubuntu的简单操作

什么是 Ubuntu&#xff1f; Ubuntu&#xff08;乌帮图&#xff09;是一个非洲词汇&#xff0c;它的意思是“人性对待他人”或“群在故我在”。Ubuntu发行版将Ubuntu精神带到软件世界之中。 目前已有大量各种各样基于GNU/Linux的操作系统&#xff0c;例如:Debian,SuSE,Gentoo,R…...

标准日志插件项目【C/C++】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;项目日记_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;项目介…...

SpingBoot原理

SpingBoot原理 在前面十多天的课程当中&#xff0c;我们学习的都是web开发的技术使用&#xff0c;都是面向应用层面的&#xff0c;我们学会了怎 么样去用。而我们今天所要学习的是web后端开发的最后一个篇章springboot原理篇&#xff0c;主要偏向于底 层原理。 我们今天的课程…...

Cout输出应用举例

Cout输出应用 在main.cpp里输入程序如下&#xff1a; #include <iostream> //使能cin(),cout(); #include <stdlib.h> //使能exit(); #include <sstream> #include <iomanip> //使能setbase(),setfill(),setw(),setprecision(),setiosflags()和res…...

java的无锁编程和锁机制

Java 的并发编程中&#xff0c;为了保证线程安全和高性能&#xff0c;采用了两种主要的同步手段&#xff1a;锁机制和无锁编程。以下是对锁机制、无锁编程、死锁及其避免的详细讲解。 一、无锁编程 无锁编程通过原子操作来避免传统锁&#xff0c;从而减少线程的上下文切换&am…...

vue实现富文本编辑器上传(粘贴)图片 + 文字

vue实现富文本编辑器上传&#xff08;粘贴&#xff09;图片 文字 1.安装插件 npm install vue-quill-editor -s2.在使用vue-quill-editor富文本的时候&#xff0c;对于图片的处理经常是将图片转换成base64&#xff0c;再上传数据库&#xff0c;但是base64不好存储。 原理&a…...

子集和全排列(深度优先遍历)问题

欢迎访问杀马特主页&#xff1a;小小杀马特主页呀&#xff01; 目录 前言&#xff1a; 例题一全排列&#xff1a; 1.题目介绍&#xff1a; 2.思路汇总&#xff1a; 3.代码解答&#xff1a; 例题二子集&#xff1a; 题目叙述&#xff1a; 解法一&#xff1a; 1.思路汇总…...

判断检测框是否在感兴趣区域(ROI)内

判断检测框是否在感兴趣区域&#xff08;ROI&#xff09;内 在计算机视觉和图像处理中&#xff0c;我们经常需要确定一个矩形检测框是否位于一个特定的感兴趣区域&#xff08;Region of Interest, ROI&#xff09;内。这个ROI可以是一个多边形&#xff0c;而检测框则是一个矩形…...

正点原子阿尔法ARM开发板-IMX6ULL(九)——关于SecureCRT连接板子上的ubuntu

文章目录 一、拨码器二、SecureCRT 一、拨码器 emmm,也是好久没学IMX6ULL了&#xff0c;也是忘了拨码器决定了主板的启动方式 一种是直接从TF卡中读取文件&#xff08;注意这里是通过imdownload软件编译好了之后&#xff0c;通过指令放入TF卡&#xff09; 一种是现在这种用串口…...

微信支付Java+uniapp微信小程序

JS&#xff1a; request.post(/vip/pay, {//这是自己写的java支付接口id: this.vipInfo.id,payWay: wechat-mini}).then((res) > {let success (res2) > {//前端的支付成功回调函数this.$refs.popup.close();// 支付成功刷新当前页面setTimeout(() > {this.doGetVipI…...

【NOIP提高组】加分二叉树

【NOIP提高组】加分二叉树 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 设一个n个节点的二叉树tree的中序遍历为&#xff08;l,2,3,…,n&#xff09;&#xff0c;其中数字1,2,3,…,n为节点编号。每个节点都有一个分数&#xff08;均为正整…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

全面解析各类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&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...