★ C++进阶篇 ★ C++11(上)

news2024/11/6 7:09:58

Ciallo~(∠・ω< )⌒☆ ~ 今天,我将和大家一起学习C++11 ~

​❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️

澄岚主页:椎名澄嵐-CSDN博客

C++基础篇专栏:★ C++基础篇 ★_椎名澄嵐的博客-CSDN博客

C++进阶篇专栏:★ C++进阶篇 ★_椎名澄嵐的博客-CSDN博客

​❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️❄️

目录

壹  C++11的发展历史

贰  列表初始化

2.1 C++98传统的{}

2.2 C++11中的{}

2.3 C++11中的std::initializer_list

叁  右值引用和移动语义

3.1 左值和右值

3.2 左值引用和右值引用

3.3 引用延长生命周期

3.4 左值和右值的参数匹配

3.5 右值引用和移动语义的使用场景

3.5.1左值引用主要使用场景回顾

3.5.2 移动构造和移动赋值

3.5.3 右值引用和移动语义解决传值返回问题

3.6 类型分类

3.7 引用折叠

3.8 完美转发


壹  C++11的发展历史

C++11是C++的第二个主要版本,并且是从C++98起的最重要更新。它引入了大量更改,标准化了既有实践,并改进了对C++程序员可用的抽象。在它最终由ISO在2011年8月12日采纳前,⼈们曾使用名称“C++0x”,因为它曾被期待在2010年之前发布。C++03与C++11期间花了8年时间,故而这是迄今为止最⻓的版本间隔。从那时起,C++有规律地每3年更新⼀次


贰  列表初始化

2.1 C++98传统的{}

C++98中⼀般数组和结构体可以用{}进行初始化。

struct Point
{
	int _x;
	int _y;
};
int main()
{
	int array1[] = { 1, 2, 3, 4, 5 };
	int array2[5] = { 0 };
	Point p = { 1, 2 };
}

2.2 C++11中的{}

  • C++11以后想统⼀初始化方式,试图实现⼀切对象皆可用{}初始化{}初始化也叫做列表初始化
  • 内置类型支持,⾃定义类型也支持,自定义类型本质是类型转换,中间会产⽣临时对象,最后优化了以后变成直接构造
  • {}初始化的过程中,可以省略掉=。
  • C++11列表初始化的本意是想实现⼀个大统⼀的初始化方式,其次他在有些场景下带来的不少便利,如容器push/inset多参数构造的对象时,{}初始化会很方便
// ⼀切皆可⽤列表初始化,且可以不加=
int main()
{
    // C++98支持的
    int a1[] = { 1, 2, 3, 4, 5 };
    int a2[5] = { 0 };
    Point p = { 1, 2 };

    // C++11支持的
    // 内置类型⽀持
    int x1 = { 2 };
    int x2 = 2;

    // ⾃定义类型支持
    // 这里本质是用{ 2025, 1, 1 }构造⼀个Date临时对象
    // 临时对象再去拷⻉构造d1,编译器优化后合⼆为⼀变成{ 2025, 1, 1 }直接构造初始化d1
    // 运行⼀下,我们可以验证上面的理论,发现是没调用拷⻉构造的
    Date d1 = { 2025, 1, 1 };

    // 这⾥d2引⽤的是{ 2024, 7, 24 }构造的临时对象
    // 临时对象有常性,加const
    const Date& d2 = { 2024, 7, 24 };

    // 需要注意的是 C++98⽀持单参数时类型转换,也可以不用{}
    Date d3 = { 2025 };
    Date d4 = 2025;

    // 可以省略掉=
    Point p1{ 1, 2 };
    int x3{ 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;
}

2.3 C++11中的std::initializer_list

C++11库中提出了⼀个std::initializer_list的类,,这个类的本质是底层开⼀个数组,将数据拷贝过来,std::initializer_list内部有两个指针分别指向数组的开始和结束。

template<class T>
class vector {
public:
    typedef T* iterator;
    vector(initializer_list<T> l)
    {
        for (auto e : l)
            push_back(e)
    }
private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _endofstorage = nullptr;
};
// 另外,容器的赋值也⽀持initializer_list的版本
vector& operator= (initializer_list<value_type> il);
map& operator= (initializer_list<value_type> il);
// 构造 + 拷贝构造 + 优化  可以去掉=
//vector<int> v1 = { 1,2,3,4 };
//vector<int> v2 = { 1,1,1,1,1,1,1,1,1,1,1 };
//const vector<int>& v3 = { 7,7,7,7,7,7,7,7,7,7,7 };
vector<int> v1{ 1,2,3,4 };
vector<int> v2{ 1,1,1,1,1,1,1,1,1,1,1 };
const vector<int>& v3{ 7,7,7,7,7,7,7,7,7,7,7 };

// 构造
vector<int> v4({ 0,7,2,1 });

initializer_list<int> il1 = { 4,2,0,6,2 };
int arr[] = { 7,4,7,5,1 };

// pair对象的{}初始化隐式类型转换和map的initializer_list构造结合到⼀起
map<string, string> dict = { {"xxx", "yyy"}, {"aaa", "zzz"} };

叁  右值引用和移动语义

C++98的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,C++11之后我们之前学习的引用就叫做左值引用。⽆论左值引用还是右值引用,都是给对象取别名

3.1 左值和右值

左值是一个表示数据的表达式(如变量名或解引用的指针),⼀般是有持久状态,存储在内存中,我 们可以获取它的地址,左值可以出现赋值符号的左边,也可以出现在赋值符号右边。定义时const 修饰符后的左值,不能给他赋值,但是可以取它的地址。

// 左值:可以取地址、
// 以下的p、b、c、* p、s、s[0]就是常⻅的左值
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("Ciallo~");
s[0] = 'X'; // 调用operator=,传引用返回

cout << &c << endl; // 0000006A9E8FFC54
cout << (void*)&s[0] << endl; // 0000006A9E8FFC80
cout << &s[0] << endl; // Xiallo~(默认返回char*)

右值也是一个表示数据的表达式,要么是字面值常量、要么是表达式求值过程中创建的临时对象 等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边右值不能取地址

// 右值:不能取地址
double x = 1.1, y = 2.2;
// 以下几个10、x + y、fmin(x, y)、string("11111")都是常⻅的右值
10;
x + y;
fmin(x, y); // 传值返回
string("11111");

左值的英文简写为lvalue右值的英文简写为rvalue。传统认为它们分别是left value、right value 的缩写。现代C++中,lvalue被解释为loactevalue的缩写,可意为存储在内存中、有明确存储地址可以取地址的对象,而rvalue被解释为readvalue,指的是那些可以提供数据值,但是不可以寻址,例如:临时变量,字⾯量常量,存储于寄存器中的变量等,也就是说左值和右值的核心区别就是能否取地址 ~

3.2 左值引用和右值引用

Type& r1 = x; Type&& rr1 = y; 

第⼀个语句就是左值引用,左值引用就是给左值取别名,第⼆个就是右值引用,同样的道理,右值引用就是给右值取别名

// 左值引用给左值取别名
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左值引用可以引用右值
const int& rx1 = 10;
const double& rx2 = x + y;
const double& rx3 = fmin(x, y);
const string& rx4 = string("11111");

template <class T>
void zmcl(const T& x) // 模板这样写既可以传左值也可以传右值
{}

右值引用不能直接引用左值,但是右值引用可以引用move(左值) ~ move是库里面的⼀个函数模板,本质内部是进行强制类型转换~

// 右值引用不能直接引用左值,但是右值引用可以引用move(左值)
int&& rrx1 = move(b);
int*&& rrx2 = move(p);
int&& rrx3 = move(*p);
string&& rrx4 = move(s);
string&& rrx5 = (string&&)s;// 底层就是强转
cout << &b << endl;// 不会改变本身属性
cout << &r1 << endl;
cout << &rr1 << endl

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

// 这里要注意的是,rr1的属性是左值,所以不能再被右值引用绑定,除非move⼀下
int& r6 = r1;
// int&& rrx6 = rr1;报错
int&& rrx6 = move(rr1);

3.3 引用延长生命周期

右值引用可用于为临时对象延长生命周期,const的左值引用也能延长临时对象生存期,但这些对象无法被修改。

int main()
{
    string s1 = "Test";
    // string&& r1 = s1;  
    const string& r2 = s1 + s1;
    // r2 += "Test";           
    string&& r3 = s1 + s1;
    r3 += "Test";
    cout << r3 << '\n';
    return 0;
}

3.4 左值和右值的参数匹配

  • C++98中,我们实现⼀个const左值引用作为参数的函数,那么实参传递左值和右值都可以匹配
  • C++11以后,分别重载左值引用、const左值引用、右值引用作为形参的f函数,那么实参是左值会匹配f(左值引用),实参是const左值会匹配f(const左值引用),实参是右值会匹配f(右值引用)。
  • 右值引用变量在用于表达式时属性是左值
void f(int& x)
{
    std::cout << "左值引用重载f(" << x << ")\n";
}
void f(const int& x)
{
    std::cout << "到const的左值引用重载f(" << x << ")\n";
}
void f(int&& x)
{
    std::cout << "右值引用重载f(" << x << ")\n";
}
int main()
{
    int i = 1;
    const int ci = 2;
    f(i);  // 调用f(int&)
    f(ci); //  调用f(const int&)
    f(3);  // 调用f(int&&),如果没有f(int&&)重载则会调用f(const int&)
    f(std::move(i)); // 调用f(int&&)

    // 右值引用变量在用于表达式时是左值
    int&& x = 1;
    f(x);            // 调用f(int& x)
    f(std::move(x)); // 调用f(int&& x)

    return 0;
}

3.5 右值引用和移动语义的使用场景

3.5.1左值引用主要使用场景回顾

左值引用主要使用场景是在函数中左值引用传参左值引用传返回值时减少拷贝,同时还可以修改实参和修改返回对象的值。左值引用已经解决大多数场景的拷贝效率问题,但是有些场景不能使用传左值引用返回,如addStrings和generate函数,C++98中的解决方案只能是被迫使用输出型参数解决。那么C++11以后这里可以使用右值引用做返回值解决吗?显然是不可能的,因为这里的本质是返回对象是⼀个局部对象函数结束这个对象就析构销毁了,右值引用返回也无法概念对象已经析构销毁的事实。

// 传值返回需要拷⻉
string addStrings(string num1, string num2) 
{
    string str;
    int end1 = num1.size() - 1, end2 = num2.size() - 1;
    // 进位
    int next = 0;
    while (end1 >= 0 || end2 >= 0)
    {
        int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;
        int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;
        int ret = val1 + val2 + next;
        next = ret / 10;
        ret = ret % 10;
        str += ('0' + ret);
    }
    if (next == 1)
        str += '1';
    reverse(str.begin(), str.end());
    return str;
}
// 这⾥的传值返回拷⻉代价就太⼤了
vector<vector<int>> generate(int numRows)
{
    vector<vector<int>> vv(numRows);
    for (int i = 0; i < numRows; ++i)
    {
        vv[i].resize(i + 1, 1);
    }
    for (int i = 2; i < numRows; ++i)
    {
        for (int j = 1; j < i; ++j)
        {
            vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
        }
    }
    return vv;
}

3.5.2 移动构造和移动赋值

  • 移动构造函数是⼀种构造函数,类似拷贝构造函数,移动构造函数要求第⼀个参数是该类类型的引用,但是不同的是要求这个参数是右值引用,如果还有其他参数,额外的参数必须有缺省值
string(const string& s) // 正常构造
    :_str(nullptr)
{
    cout << "string(const string& s) -- 拷⻉构造" << endl;
    reserve(s._capacity);
    for (auto ch : s)
    {
        push_back(ch);
    }
}
// 移动构造
string(string&& s)
{
    cout << "string(string&& s) -- 移动构造" << endl;
    // 掠夺右值的资源,在销毁前转移走
    swap(s);
}

zmcl::string s1 = ("1111111"); // string(char* str)构造
zmcl::string s2 = s1;          // string(const string& s) -- 拷贝构造
zmcl::string s3 = zmcl::string("7777777"); // string(char* str)构造
zmcl::string s4 = move(s1);    // string(string&& s) -- 移动构造
  • 移动赋值是⼀个赋值运算符的重载,他跟拷贝赋值构成函数重载,类似拷贝赋值函数,移动赋值函数要求第一个参数是该类类型的引用,但是不同的是要求这个参数是右值引用。
string& operator=(const string& s)
{
    //cout << "string& operator=(const string& s) -- 拷贝赋值" << endl;
    if (this != &s)
    {
        _str[0] = '\0';
        _size = 0;
        reserve(s._capacity);
        for (auto ch : s)
        {
            push_back(ch);
        }
    }
    return *this;
}
// 移动赋值
string& operator=(string&& s)
{
    cout << "string& operator=(string&& s) -- 移动赋值" << endl;
    swap(s);
    return *this;
}
  • 对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类移动构造和移动赋值才有意义,因为移动构造和移动赋值的第⼀个参数都是右值引用的类型,他的本质是要“窃取”引用的右值对象的资源,而不是像拷贝构造和拷贝赋值那样去拷贝资源,从而提高效率。

3.5.3 右值引用和移动语义解决传值返回问题

1. 右值对象构造,只有拷贝构造,没有移动构造的场景:

左边为不优化的情况下,两次拷贝构造,右边为编译器优化的场景下连续步骤中的拷贝合⼆为⼀变为⼀次拷贝构造

vs2019的release和vs2022的debug和release,下面代码优化为非常恐怖,会直接将str对象的构造,str拷贝构造临时对象,临时对象拷贝构造ret对象,合三为一,变为直接构造

2. 右值对象构造,有拷贝构造,也有移动构造的场景:

左边为不优化的情况下,两次移动构造,右 边为编译器优化的场景下连续步骤中的拷贝合⼆为⼀变为一次移动构造

vs2019的release和vs2022的debug和release会直接将str对象的构造,str拷贝构造临时对象,临时对象拷贝构造ret对象,合三为一,变为直接构造

3.右值对象赋值,只有拷贝构造和拷贝赋值,没有移动构造和移动赋值的场景与右值对象赋值,既有拷贝构造和拷贝赋值,也有移动构造和移动赋值的场景和上面的构造对比差不多~

3.6 类型分类

  • C++11以后,进⼀步对类型进行了划分,右值被划分纯右值(purevalue)和将亡值 (expiring value)。
  • 纯右值是指那些字面值常量或求值结果相当于字面值或是一个不具名的临时对象。如:42、 true 、 nullptr 或者类似str.substr(1, 2) 、 str1 + str2 传值返回函数调用,或者整型a 、 b , a++ , a+b 等。纯右值和将亡值C++11中提出的,C++11中的纯右值概念划分等价于 C++98中的右值。
  • 将亡值是指返回右值引用的函数的调用表达式和转换为右值引用的转换函数的调用表达,如 move(x) 、 static_cast(x)。
  • 泛左值(generalizedvalue,简称glvalue),泛左值包含将亡值和左值。

3.7 引用折叠

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

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的类型是nt&&

以下代码func1由于引用折叠限定,实例化以后总是⼀个左值引用

template<class T>
void func1(T& x)
{}

int main()
{
    int n = 0;
    // 没有折叠->实例化为void f1(int& x)
    func1<int>(n);
    // func1<int>(0);  // 报错

    // 折叠->实例化为void f1(int& x)
    func1<int&>(n);
    // func1<int&>(0); // 报错

    // 折叠->实例化为void f1(int& x)
    func1<int&&>(n);
    // func1<int&&>(0); // 报错

    // 折叠->实例化为void f1(const int& x)
    func1<const int&>(n);
    func1<const int&>(0);

    // 折叠->实例化为void f1(const int& x)
    func1<const int&&>(n);
    func1<const int&&>(0);

    return 0;
}

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

template<class T>
void func2(T&& x)
{}

int main()
{
    int n = 0;
    // 没有折叠->实例化为void f2(int&& x)
    // func2<int>(n);   // 报错
    func2<int>(0);

    // 折叠->实例化为void f2(int& x)
    func2<int&>(n);
    // func2<int&>(0);  // 报错

    // 折叠->实例化为void f2(int&& x)
    // func2<int&&>(n); // 报错
    func2<int&&>(0);
    return 0;
}

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

template<class T>
void Func(T&& t)
{
    int a = 0;
    T x = a;
    // x++;
    cout << &a << endl;
    cout << &x << endl;
}

int main()
{
    // 10是右值,推导出T为int,模板实例化为void Function(int&& t)
    Func(10); // 右

    // a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)
    int a;
    Func(a); // 左
    
    // std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)
    Func(std::move(a)); // 右

    // b是左值,推导出T为const int&,引用折叠,模板实例化为void Function(const int& t)
    // 所以Function内部会编译报错,x不能++
    const int b = 8;
    Func(b); // const左

    // std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&& t)
    // 所以Function内部会编译报错,x不能++
    Func(std::move(b)); // const右

    return 0;
}

3.8 完美转发

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

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

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()
{
    int a;
    const int b = 8;
    // 10是右值,推导出T为int,模板实例化为void Function(int&& t)
    Function(10);// 右值

    // a是左值,推导出T为int&,引用折叠,模板实例化为void Function(int& t)
    Function(a);// 左值
       
    // std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)
    Function(std::move(a));// 右值
    
    // b是左值,推导出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;
}

~ 完 ~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2234130.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux 28】应用层协议 - HTTPS

文章目录 &#x1f308; 一、HTTPS 相关概念⭐ 1. 什么是 HTTPS⭐ 2. 加密 & 解密 & 密钥⭐ 3. 常见的加密方式⭐ 4. 数据摘要 & 数据指纹⭐ 5. 初识数字签名 &#x1f308; 二、HTTPS 的加密方案探究⭐ 1. 方案一&#xff1a;只使用对称加密⭐ 2. 方案二&#xff…

qt QFileDialog详解

1、概述 QFileDialog是Qt框架中的一个对话框类&#xff0c;用于提供一个标准的文件选择对话框。它允许用户浏览文件系统&#xff0c;选择一个或多个文件或目录&#xff0c;以及指定文件名。QFileDialog支持本地文件系统和远程文件系统&#xff08;如通过FTP或SFTP访问的文件系…

C语言不同基本数据类型占用字节大小和取值范围

具体请前往&#xff1a;C语言各种基本数据类型字节大小和取值范围

Vue3:新特性详解

本文目录&#xff1a; 1.特性函数setup2.Ref 语法3.Reactive 函数4.Vue3 生命周期5.侦测变化 - watch6.Vue3的模块化开发7.弹窗类组件优化&#xff1a;Teleport8.异步组件优化&#xff1a;Suspense9.全局API优化 1.特性函数setup 1、setup函数是处于 生命周期函数 beforeCreate…

Linux scp命令 | 菜鸟教程-从本地复制到远程/从远程复制到本地

目录 Linux scp命令 语法 实例 1、从本地复制到远程 2、从远程复制到本地 说明 文章来源&#xff1a;Linux scp命令 | 菜鸟教程 Linux scp命令 nux scp 命令用于 Linux 之间复制文件和目录。 scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远…

计算机毕业设计Python+大模型膳食推荐系统 知识图谱 面向慢性病群体的膳食推荐系统 健康食谱推荐系统 机器学习 深度学习 Python爬虫 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

大数据集群中实用的三个脚本文件解析与应用

目录 一、jps - cluster.sh 脚本文件 &#xff08;一&#xff09;背景与功能 &#xff08;二&#xff09;使用方法 二、集群文件分发脚本 xsync.sh &#xff08;一&#xff09;背景与问题 &#xff08;二&#xff09;功能与实现原理 &#xff08;三&#xff09;脚本编写…

【数据分享】1981-2024年我国逐日平均气温栅格数据(免费获取)

气象数据一直是一个价值很高的数据&#xff0c;它被广泛用于各个领域的研究当中。这其中&#xff0c;又以平均气温数据最为常用&#xff01;之前我们分享过来源于美国国家海洋和大气管理局&#xff08;NOAA&#xff09;下设的国家环境信息中心(NCEI)发布的1929-2024年全球站点的…

多模态PaliGemma——Google推出的基于SigLIP和Gemma的视觉语言模型

前言 本文怎么来的呢&#xff1f;其实很简单&#xff0c;源于上一篇文章《π0——用于通用机器人控制的流匹配VLA模型&#xff1a;一套框架控制7种机械臂(改造了PaliGemma和ACT的3B模型)》中的π0用到了PaliGemma 故本文便来解读下这个PaliGemma 第一部分 PaliGemma 1.1 Pal…

开源模型应用落地-glm模型小试-glm-4-9b-chat-Gradio集成(三)

一、前言 GLM-4是智谱AI团队于2024年1月16日发布的基座大模型&#xff0c;旨在自动理解和规划用户的复杂指令&#xff0c;并能调用网页浏览器。其功能包括数据分析、图表创建、PPT生成等&#xff0c;支持128K的上下文窗口&#xff0c;使其在长文本处理和精度召回方面表现优异&a…

ABAP开发学习——OLE

目录 1.概览 2.OEL的实现方式 3.OLE的配置 4.OLE的实现 4.1 OLE Demo 4.2 OLE实现的步骤 5.OLE实现样例 5.1 OLE中的的类型转换 5.2 变量定义 5.3 创建对象 5.4 表单页的操作 5.5 设置文件保存路径 5.6 文件是否可见 5.7 单元格操作 5.7.1 给单元格赋值 5.7.2…

在Java中,实现数据库连接通常使用JDBC

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

单臂路由实现不同VLAN之间设备通信

转载请注明出处 本实验为单臂路由配置&#xff0c;目的为让不同VLAN之间的设备能够互相通信。 1.首先&#xff0c;按照要求配置两个pc的ip地址&#xff0c;以pc0为例子&#xff1a; 2在交换机创建vlan10和vlan20 3.划分vlan&#xff0c;pc0为vlan10的设备&#xff0c;pc1为vla…

前后端交互通用排序策略

目录 排序场景 排序实现思路 1. 静态代码排序实现 2.数据库驱动排序实现 3. 基于Java反射的动态排序实现 通用排序工具 SortListUtil 结语 排序场景 在面向前端数据展示的应用场景中&#xff0c;我们旨在实现一个更加灵活的排序机制&#xff0c;该机制能够支持对从后端传递…

【华为云-云驻共创】UCS跨云多活容灾:让业务高可用不再是难题

【摘要】云原生应用深入到企业各个业务场景&#xff0c;云原生正在走向分布式化&#xff0c;跨云跨域统一协同治理&#xff0c;保证一致应用体验&#xff0c;这些新的需求日益凸显。而容灾是确保服务高可用的保障&#xff0c;但即使应用部署在云上&#xff0c;也无法避免市政方…

Linux内核编程(十九)SPI子系统的应用与驱动编写

本文目录 一、 SPI驱动框架图二、编写SPI驱动device框架三、编写SPI驱动driver框架四、实验一编写mcp2515驱动1. 注册字符设备或杂项设备框架2. SPI写数据3. SPI读寄存器数据 4. MCP2515相关配置 对于SPI基础知识这里不做过多讲解&#xff0c;详情查看&#xff1a;SPI基础知识实…

谍影重重5.0

打开流量包可以发现&#xff0c;流量中含有大量的smb加密通信&#xff0c;并且使用了ntlm v2加密协议进行身份认证 包过滤ntlmssp 认证后smb协议进行了大量的数据传输 取出tom的包内数据 得到以下数据 username:tomdomain: .NTProofStr: ca32f9b5b48c04ccfa96f35213d63d75NT…

qt QAbstractItemModel详解

1. 概述 QAbstractItemModel是Qt框架中的一个核心抽象基类&#xff0c;在Qt的模型/视图架构中扮演着至关重要的角色。这个类提供了一个接口&#xff0c;用于表示和管理数据&#xff0c;但不直接处理数据的存储。它的主要功能是为视图组件&#xff08;如QListView、QTableView和…

华为OD机试 - 无重复字符的元素长度乘积的最大值(Python/JS/C/C++ 2024 C卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

【C++之STL】一文学会使用 string

文章目录 1. STL导读1. 1 什么是STL1. 2 STL的版本1. 3 STL六大组件1. 4 STL的重要性1. 5 STL的学习1. 6 STL系列博客的规划 2. string2. 1 为什么学习string类?2. 2 标准库中的string2. 3 基本构造2. 4 尾插与输出运算符重载2. 5 构造函数2. 6 赋值运算符重载2. 7 容量操作2.…