【C++11】lambda匿名函数和包装器

目录

一,lambda匿名函数

1-1,lambda的引入

1-2,lambda表达式书写格式

1-3,lambda函数的名称

1-4,lambda捕获列表的使用

1-5,函数对象与lambda表达式

二,包装器

2-1,function包装器

2-2,bind包装器


一,lambda匿名函数

1-1,lambda的引入

        在C++中,lambda函数是一种简洁的匿名函数或表达式,能够轻松处理复杂的逻辑和数据操作。lambda匿名函数可替换复杂的函数指针或伪函数,可以解决复杂而繁琐的仿函数和函数指针的使用,以及让程序员能够将类似于函数的表达式用作接收函数指针或伪函数的函数的参数(这点与function的作用有关)。      

1-2,lambda表达式书写格式

        lambda匿名函数:[capture-list] (parameters) mutable -> return-type { statement }

        [capture-list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量(捕获变量的值或引用‘&’)供lambda函数的函数体使用。

        (parameters):参数列表,与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。

        mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。lambda常性限制的是捕捉列表中的参数,没有限制参数列表中的参数。

        ->returntype:lambda函数的返回值类型(注意:不是表达式返回类型,下面会详细解释这方面),没有返回值时此部分可省略。由于lambda返回值类型相当于使用decltype根据返回值推断得到,如果lambda不包含返回语句,推断出的返回类型将为void,因此返回类型除非必要,一般不需要写。若使用该指定返回类型,参数列表将不可省略(即使参数为空)。

        {statement}:lambda函数的函数体,与普通函数的函数体一样,包含实现功能的代码。

注意:在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为 空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

#include <iostream>
#include <vector>  
#include <algorithm>
using namespace std;
struct Goods
{
    string _name;  // 名字
    double _price; // 价格
    int _evaluate; // 评价
    Goods(const char* str, double price, int evaluate)
        :_name(str)
        , _price(price)
        , _evaluate(evaluate)
    {}
};
struct ComparePriceLess //仿函数
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price < gr._price; 
    }
};
struct ComparePriceGreater //仿函数
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price > gr._price;
    }
};
int main()
{
    vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
   3 }, { "菠萝", 1.5, 4 } };
    //下面两个排序需要编写两个访函数
    sort(v.begin(), v.end(), ComparePriceLess()); //按价格从小到大排序
    sort(v.begin(), v.end(), ComparePriceGreater()); //按价格从大到小排序
    //下面两个排序使用lambda匿名函数,直接一个式子解决一种函数表达,可看出要比伪函数或函数指针(即直接函数实现)更简便
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; });
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });
    /*上面[](const Goods& g1, const Goods& g2) {return g1._price < g2._price; }会自动推导函数提返回类型是bool,即与[](const Goods& g1, const Goods& g2)->bool {return g1._price < g2._price; }等效*/
    return 0;
}

1-3,lambda函数的名称

        上面的lambda使用是作为单独函数表达式,下面将lambda表达式用作名称来使用(将该匿名函数赋给一个变量,该变量就是此名称)。lambda函数表达式必须要使用auto类型来接收。lambda底层实际上是一个类的仿函数,它在C++中并不直接返回它的结果,而是创建了一个可调用的对象(一个函数对象)。因此,你不能直接将一个lambda表达式赋值给一个非函数对象类型的变量(如int)。编译器会报错,因为int类型期望一个int类型或与int型相似类型的值,但是得到了一个lambda表达式(一个类类型)。不仅如此,编译器只有在编译时才可以确定lambda生成的式子,不同编译器下生成的式子还可能还不同,因此只能用auto接收lambda表达式,这里使用typeid().name可看出。

测试一:lambda的调用原理

//调用错误,lambda函数的返回值是int,但本身返回值不是int,是一个类类型
int a = [](int x)->int {return 55 + 5l; };
//auto自动推演类型,调用正确
auto _a = [](int x)->int {return 55 + 5l; };
//赋予lambda函数表达式名称为_a,而lambda底层是类的访函数,按照仿函数的调用即可
int b = _a(5);

//上面lambda的调用相当于下面的类的仿函数调用

class A
{
public:
    int operator()(int x)
    {
        return 55 + 51;
    }
};

A _a;
int b = _a(5);

[](int x)->int {return 55 + 5l; };的调用相当于int x;A()(x);的调用

测试二:lambda原理调用的示例

#include <iostream>
using namespace std;

int main()
{
    int x = 10;
    //使用lambda函数捕获x的引用并修改它  
    auto m = [&x]() {x = x * x; }; //m是一个实现访函数的类
    m(); //lambda函数的调用 
    cout << "x: " << x << endl;  
    cout << typeid(m).name() << endl; 
    return 0;
}

        lambda表达式是一个匿名函数,也可理解为一个表达式,该函数无法直接调用,如果想要直接调用,通常需要借助auto将其赋值给一个变量,通过该名称变量调用。

1-4,lambda捕获列表的使用

        捕获列表说明:捕捉列表用于传入上下文中的数据(传值或引用),以便供lambda使用。这里捕获数据的方式有以下几种:

        [var]:表示值传递方式捕捉变量var

        [&var]:表示引用传递捕捉变量var

        [=]:表示值传递方式捕获当前作用域中所有的变量(包括this)

        [&]:表示引用传递捕获当前作用域中所有的变量(包括this)

        [this]:表示值传递方式捕捉当前的this指针

#include <iostream>
using namespace std;
int main()
{    
    int a = 10, b = 10;
    //值传递捕捉a,捕捉的是a的拷贝
    auto fun1 = [a, b]()mutable { a += 1; b += 1; return a + b; }; /*lambda默认是const函数,限制了捕捉列表的参数,这里要使用mutable取消对捕捉列表的限制*/
    cout << "fun1: a + b = " << fun1() << endl;
    cout << "a + b = " << a + b << endl;
    cout << endl;
    //引用传递捕捉b,捕捉的是b的引用
    auto fun2 = [&a, &b] {a += 1; b += 1; return a + b; }; /*捕捉列表引用捕捉,说明开发者想要对其修改,lambda没有对捕捉列表限制,可以修改*/
    cout << "fun2: a + b = " << fun2() << endl;
    cout << "a + b = " << a + b << endl;
    cout << endl;
    //值传递获取当前作用域所有变量,捕捉所有变量的拷贝
    int c = 10, d = 10;
    auto fun3 = [=]()mutable {c += 1; d += 1; return c + d; }; //与上面“值传递捕捉a”同理
    cout << "fun3: c + d = " << fun3() << endl;
    cout << "c + d = " << c + d << endl;
    cout << endl;
    //引用传递获取当前作用域所有变量,捕捉所有变量的引用
    auto fun4 = [&] {c += 1; d += 1; return c + d; }; //与上面“引用传递捕捉b”同理
    cout << "fun4: c + d = " << fun4() << endl;
    cout << "c + d = " << c + d << endl;
    cout << endl;
    //捕捉a的拷贝,其它数据的引用
    auto fun5 = [&, a]()mutable {a = 1; b = 1; c = 1; return a + b + c; };
    /*auto fun6 = [=, &a]()mutable {a = 1; b = 1; c = 1; return a + b + c; };这里也可捕捉a的引用,其它数据的拷贝*/
    cout << "fun5: a + b + c = " << fun5() << endl;
    cout << "a + b + c = " << a + b + c;
    cout << endl;
    return 0;
}

        这块需说明以下几个注意点:

        1,语法上捕捉列表可由多个捕捉项组成,并以逗号分割,上面最后一个例子运用的就是此原理。还有比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量。

        2,捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。

        4,在作用域中的lambda函数仅能捕捉当前作用域中局部变量,捕捉任何非此作用域或者
非局部变量都会导致编译报错。

        5,lambda表达式之间不能相互赋值,即使看起来类型相同,因为无论怎样lambda的类型都不可能一样,它的底层是类的仿函数,编译时确定的类型会有所不同。

        6,lambda底层实现了拷贝构造,但是禁掉了默认构造。

#include <iostream>
using namespace std;
void (*PF)(); //函数指针的声明
int main()
{
    auto a = []{cout << "Hello C++" << endl; };
    auto b = []{cout << "Hello C++" << endl; };

    /*a = b; 编译失败,lambda实现虽都一样,但两者的类型不一样,内部不存在不同类型间的赋值。从下面的输出可看出*/
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << endl;
    /*允许使用一个lambda表达式拷贝构造一个新的副本,两者的类型相同,但lambda对象不能默认实现构造*/

    //decltype(a) c; 默认构造的调用,编译失败
    auto c(a);
    cout << typeid(c).name() << endl;
    cout << typeid(a).name() << endl;
    cout << endl;
    c();
    //也可以将lambda表达式赋值给相同类型的函数指针
    PF = a;
    PF();
    return 0;
}

1-5,函数对象与lambda表达式

        C++的函数对象通常指的是仿函数。lambda与函数对象极为相似,实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator(),即仿函数。下面的lambda表达式与函数对象基本可理解为一体。

class Rate
{
public:
    Rate(double rate) : _rate(rate) {}
    double operator()(double money, int year)
    {
        return money * _rate * year;
    }
private:
    double _rate;
};
int main()
{
    //函数对象
    double rate = 0.49;
    Rate r1(rate);
    r1(10000, 2);
    //lamber表达式
    auto r2 = [=](double monty, int year)->double {return monty * rate * year; };
    r2(10000, 2);
    return 0;
}


二,包装器

2-1,function包装器

        function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。由于函数指针较为复杂,仿函数比较单一,只能实现一种功能,如上Goods的比较,lambda函数语法层中没有类型,auto接收的只是名称,它们各有特色,但比较零散。function包装器的主要作用是封装函数指针(包括普通函数)、函数对象(仿函数)、lambda函数(匿名函数),将它们同一类型,实现一个函数的调用。我们先观察下面代码。

代码一:

#include <iostream>
using namespace std;
template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}
double f(double i)
{
    return i / 2;
}
struct Functor
{
    double operator()(double d) 
    {
        return d / 3;
    }
};
int main()
{
    //函数名(函数指针)
    cout << useF(f, 11.11) << endl;
    cout << endl;
    //函数对象(仿函数)
    cout << useF(Functor(), 11.11) << endl;
    cout << endl;
    //lamber表达式
    cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
    cout << endl;
    return 0;
}

代码二:

#include <iostream>
using namespace std;
template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}
int f1(double i) { return i / 2; }
double f2(double i) { return i / 3; }
double f3(double i) { return i / 4; }
double f4(int i) { return i / 5; }
int main()
{
    /*实例化出的函数返回类型、参数类型相同,输出的count相同,即同一个函数,但若是其中一个类型不同,将会实例化出不同的函数,即输出count不同*/
    cout << useF(f1, 11.11) << endl;
    cout << useF(f2, 11.11) << endl;
    cout << useF(f3, 11.11) << endl;
    cout << useF(f4, 11.11) << endl;
    return 0;
}

        通过上面的程序验证,我们会发现对于函数模板而言,当返回类型和参数类型一致的情况下,编译器不会重新实例化出一份新的函数,但对于函数指针、函数对象、lambda表达式而言,即便三者的函数返回类型、形参类型都相同,但useF函数模板还是实例化了三份。包装器function可以很好的解决上面的问题,将它们封装成一种函数。

std::function在头文件<functional>
类模板结构原型如下
        template <class T> function;    
        template <class Ret, class... Args>
        class function<Ret(Args...)>;
模板参数说明:
        Ret : 被调用函数的返回类型
        Args…:被调用函数的形参类型

使用方法如下:

#include <iostream>
#include<functional>
using namespace std;

template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f1(double i) { return i / 2; }
int f2(double i) { return i / 2; }
double f3(int i) { return i / 3; }

struct Functor
{
    double operator()(double d) { return d / 3; }
};

int main()
{
    //函数指针
    function<double(double)> fc1 = f1;
    fc1(11.11); //将f1封装成fc1,返回类型double、形参类型double
    cout << useF(fc1, 11.11) << endl;
    /*下面的函数指针形式输出发现与上面的不同,返回类型和参数类型只要有一个不同,函数模板实例化出的函数就不同*/
    function<double(int)> fcc1 = f2;  
    fcc1(11.11); //将f2封装成fc,此时返回类型int、形参类型double,与f2不同
    cout << useF(fcc1, 11.11) << endl;
    //封装函数返回类型和形参类型相同,调用函数相同
    function<double(double)> fcc2 = f3;
    fcc2(11.11); //将f3封装成fc2,此时返回类型double、形参类型double,与f3不同
    cout << useF(fcc2, 11.11) << endl;
    cout << endl;

    //函数对象
    function<double(double)> fc2 = Functor();
    fc2(11.11);
    cout << useF(fc2, 11.11) << endl;

    //lambda表达式
    function<double(double)> fc3 = [](double d)->double { return d / 4; };
    fc3(11.11);
    cout << useF(fc3, 11.11) << endl;

    return 0;
}

        function包装器封装时的函数返回类型与形参类型可以与原函数不同,当包装器包装后,此时的调用情况与上面代码二一样,即若函数返回类型和形参类型相同将不会再新实例化出一份函数。

        function包装器包装后的调用与原函数互不影响,如上fc1与fc两者调用的函数不同,这里可放心使用。

类的成员函数的包装

        类的成员函数分为静态成员函数和非静态成员函数。静态成员函数没有包含隐藏的this指针,函数名即为函数地址,包装时跟其它函数包装一样,没有任何变化。非静态成员函数由于第一个参数是隐藏的this指针,所以语法规定包装时第一个形参必须是对象的指针或对象,其次,类的成员函数名本身不是地址,所以这里传递时必须传递地址,即“&”。

#include <iostream>
#include<functional>
using namespace std;
class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }
    double plusd(double a, double b)
    {
        return a + b;
    }
};

int f(int a, int b) { return a + b; }

int main()
{
    //普通函数
    function<int(int, int)> fc1 = f; 
    function<int(int, int)> f1 = &f;
    cout << fc1(1, 1) << "  " << f1(1, 1) << endl;
    cout << f << " " << &f << endl; //输出地址一样,加不加&都行

    //静态成员函数
    function<int(int, int)> fc2 = &Plus::plusi; 
    function<int(int, int)> fc = Plus::plusi;
    cout << fc2(1, 1) << "  " << fc(1, 1) << endl;
    cout << &Plus::plusi << " " << Plus::plusi << endl; //输出地址一样,加不加&都行

    //非静态成员函数
    //非静态成员函数需要对象的指针或者对象去进行调用,因为类的非静态成员函数第一个默认隐形的参数是this指针

    Plus plus;

    //对象指针。这里不能使用Plus::plusd,因为成员函数名不是地址
    function<double(Plus*, double, double)> fc3 = &Plus::plusd; 
    cout << fc3(&plus, 1, 1) << endl;  

    //对象。这里不能使用Plus::plusd

    function<double(Plus, double, double)> fc4 = &Plus::plusd;
    cout << fc4(plus, 1, 1) << "  " << fc4(Plus(), 1, 1) << endl;
    return 0;
}

2-2,bind包装器

        bind是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。它与function底层其实都是仿函数,返回的其实是一个可调用对象。

bind的作用有两个:

        1,调整可调用对象参数的顺序(通常意义不大,了解即可)。

        2,调整可调用对象参数的个数(具有一定的价值)。

bind原型结构如下

形式一:
        template <class Fn, class... Args>
        bind(Fn&& fn, Args&&... args);
形式二:(参数全部指定不能使用function接收,具体下面会说明)
        template <class Ret, class Fn, class... Args>
        bind(Fn&& fn, Args&&... args);

bind调用的一般形式:

1,auto万能接收

auto newCallable = bind(callable, arg_list); 

2,function接收

function<Ret(Args...)> newCallable = bind(callable, arg_list); 

         newCallable:是一个可调用对象,可以是函数、成员函数、函数对象或lambda表达式。

         arg_list:是一个传递给callable,并以逗号分隔的形参列表。若是直接给定 callable 的参数,当我们调用newCallable时,newCallable会调用callable,并传给指定的参数;若是 arg_list 中的参数存在占位符(placeholders占位符),则可以在稍后调用newCallable时提供这些参数。

        占位符placeholders表示参数的“占位”。占位符_1、_2等表示绑定对象被调用时应提供的参数。

#include <iostream>
#include<functional>
using namespace std;

int Sub(int a, int b)
{
    return a - b;
}

class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }

    double plusd(double a, double b)
    {
        return a - b;
    }
};

int main()
{
    //普通函数的正常参数顺序使用
    int x = 10, y = 20;
    auto f1 = bind(Sub, 10, 20);
    cout << f1() << endl; //指定参数,不需要传递

    auto f2 = bind(Sub, placeholders::_1, placeholders::_2); //成员_1表示传递Sub第一个位置参数,_2表示传递第二个位置参数
    cout << f2(x, y) << endl; //占位符使用,x传递绑定对象f2的第一个参数,y传递第二个

    /*调整参数顺序,f2第一个参数接收Sub的第二个位置上的参数,第二个参数接受Sub的第一个位置上的参数(此运用了解一下,意义不大)*/
    function<int(int, int)> f3 = bind(Sub, placeholders::_2, placeholders::_1); 
    cout << f3(x, y) << endl;

    //类的非静态成员函数绑定
    Plus p;
    /*bind绑定Plus::plusd,此处要指名地址(成员函数名称不是地址),具体绑定到p对象上,后面是绑定参数*/
    function<double(double, double)> fc4 = bind(&Plus::plusd, p, placeholders::_1, placeholders::_2);
    cout << fc4(2, 3) << endl;

    function<double(double)> fc5 = bind(&Plus::plusd, Plus(), placeholders::_1, 20); //绑定匿名对象Plus上
    cout << fc5(2) << endl;

    //类的静态成员函数绑定
    auto fc6 = bind(Plus::plusi, 10, 20); //静态成员不属于任何类,无需指名具体对象
//function<int(int, int)>fc6 = bind(Plus::plusi, 5, 7);参数全部指定,此时不能使用function

    cout << fc6() << endl;
    return 0;
}

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

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

相关文章

如何使用maven运行SpringBoot程序?

目录 一、什么是maven 二、什么是SpringBoot 三、如何使用maven运行SpringBoot程序&#xff1f; 一、什么是maven Maven&#xff1a;简化Java项目构建的自动化工具 在软件开发的世界里&#xff0c;Maven以其强大的项目管理和构建自动化功能&#xff0c;为Java开发者提供了…

容器化:ES和Kibana

1 缘起 最近在学习使用ES&#xff0c; 为了找一个功能强大的可视化工具&#xff0c;之前使用了ES-Head&#xff0c;可以满足学习需求。 闲暇时间又折腾了另一个工具Kibana&#xff0c; 分享如下。 Kibana优点&#xff1a; 用户友好性&#xff1a;Kibana提供直观易用的用户界面…

Strategy设计模式

Strategy设计模式举例。 看图&#xff1a; 代码实现&#xff1a; #include <iostream>using namespace std;class FlyBehavior { public:virtual void fly() 0; };class QuackBehavior { public:virtual void quack() 0; };class FlyWithWings :public FlyBehavior …

数据库(vb.net+OleDB+Access)简易学生信息管理系统

在我们日常生活当中&#xff0c;数据库一词往往离不开我们的编程界&#xff0c;在学校、仓库等方面起着存储数据及数据关系作用的文件。相较于Excel&#xff0c;Access可以存储无限多的记录&#xff0c;内容也十分丰富&#xff0c;例如文本、数字、日期、T&F等。而且不需要…

k8s命令式对象管理和配置

kubectl补全: # dnf install -y bash-completion # echo "source <(kubectl completion bash)" >> ~/.bashrc # kubectl completion bash > /etc/bash_completion.d/kubectl 命令式对象管理 kubectl命令 # 查看所有pod kubectl get pod # 查看某个po…

LLM——探索大语言模型在心理学方面的应用研究

1. 概述 心理学经历了多次理论变革&#xff0c;目前人工智能&#xff08;AI&#xff09;和机器学习&#xff0c;特别是大型语言模型&#xff08;LLMs&#xff09;的使用&#xff0c;预示着新研究方向的开启。本文详细探讨了像ChatGPT这样的LLMs如何转变心理学研究。它讨论了LL…

docker- 镜像 导出导入

文章目录 前言docker- 镜像 导出导入1. 导出2. 删除镜像3. 导入镜像 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

忘记“也是一门学问:机器如何忘记自己学到的知识?

在信息时代&#xff0c;我们常常希望人工智能能够学到更多的知识&#xff0c;变得更加智能。但你是否想过&#xff0c;有时候让机器"忘记"一些它学到的东西&#xff0c;也是一件很重要的事&#xff1f; 随着用户隐私保护意识的提高和相关法律法规的出台&#xff0c;…

深入理解内联函数(C语言)

目录 1.什么是内联函数2.内联函数与宏3.编译器对内联函数的处理4.参考文献 1.什么是内联函数 很多人都会知道&#xff0c;可以将比较小的函数写成内联函数的形式&#xff0c;这样会节省函数调用的开销&#xff0c;具体是什么样的开销呢&#xff1f; 一个函数在执行过程中&…

IDEA通过tomcat运行注意事项

配置run--》edit configurations 以下的A B部分要保持一致 A和B的路径要保持一致

前端vue项目遇到的问题01——那些初级问题

前端vue项目遇到的问题01——那些初级问题 1. npm install 问题1.1 依赖冲突1.1.1 详细问题1.1.2 报错原因1.1.3 解决问题1.1.3.1 方式1——无视冲突1.1.3.1 方式2——更换依赖版本 1.2 nodejs版本问题1.3 node版本正确的情况&#xff08;audit问题&#xff09;&#xff08;这个…

HTML5新特性、JS【初识JS 、JS核心语法】--学习JavaEE的day47

day47 HTML5新特性 定义文档类型 在文件的开头总是会有一个标签 语言文档类型声明方式html4<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">html5<!DOCTYPE html> 新增语义化标签 理解&…

vite-plugin-vue-devtools插件

vite-plugin-vue-devtools插件旨在帮助开发者更快地理解并调试Vue应用。它通过提供全面的功能和直观的界面&#xff0c;以图形化的方式展示应用程序状态&#xff0c;使开发者能够更方便地查看和管理Vue应用的各个方面。此外&#xff0c;该插件还支持Vue3.0版本&#xff0c;并且…

【Go专家编程——内存管理——垃圾回收】

垃圾回收 所谓的垃圾就上不在需要的内存块&#xff0c;垃圾如果不清理&#xff0c;这些内存块就没有办法再次被分配使用。在不支持垃圾回收的编程语言中&#xff0c;这些垃圾内存就上泄露的内存。 1. 垃圾回收算法 常见的垃圾回收算法有3种 引用计数&#xff1a;对每个对象…

yolov10 快速使用及训练

参考: https://docs.ultralytics.com/models/yolov10/ ultralytics其实大多数系列都能加载使用: 官方: https://github.com/THU-MIG/yolov10.git 代码参考: https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/train-yolov10-object-…

一篇文章讲透排序算法之希尔排序

希尔排序是对插入排序的优化&#xff0c;如果你不了解插入排序的话&#xff0c;可以先阅读这篇文章&#xff1a;插入排序 目录 1.插入排序的问题 2.希尔排序的思路 3.希尔排序的实现 4.希尔排序的优化 5.希尔排序的时间复杂度 1.插入排序的问题 如果用插入排序对一个逆序…

Redis 源码学习记录:集合 (set)

无序集合 Redis 源码版本&#xff1a;Redis-6.0.9&#xff0c;本篇文章无序集合的代码均在 intset.h / intset.c 文件中。 Redis 通常使用字典结构保存用户集合数据&#xff0c;字典键存储集合元素&#xff0c;字典值为空。如果一个集合全是整数&#xff0c;则使用字典国语浪费…

2024年最全的信息安全、数据安全、网络安全标准分享(可下载)

以上是资料简介和目录&#xff0c;如需下载&#xff0c;请前往星球获取&#xff1a;https://t.zsxq.com/Gz1a0

【全网最全】2024电工杯数学建模A题成品论文+前三题完整解答matlab+py代码等(后续会更新成品论文)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模A题成品论文前三题完整解答matlabpy代码等&#xff08;后续会更新成品论文&#xff09;「首先来看看目前已有的资料&am…

Sass是什么?有哪些优缺点?

目录 一、Sass是什么&#xff1f; 二、Sass的优缺点 三、Sass与SaaS 一、Sass是什么&#xff1f; Sass是世界上最成熟、最稳定、最强大的专业级CSS扩展语言。 Sass makes CSS fun again. Sass is an extension of CSS, adding nested rules, variables, mixins, selector in…