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

C/C++开发,无可避免的多线程(篇四).线程与函数的奇妙碰撞

一、函数、函数指针及函数对象

        1.1 函数

        函数(function)是把一个语句序列(函数体, function body)关联到一个名字和零或更多个函数形参(function parameter)的列表的 C++ 实体,可以通过返回或者抛出异常终止。。

// 函数名:func
// 形参列表拥有一个形参,具有名字 cnt 和类型 int
// 返回类型是 void
void func(int cnt)
{                      // 函数体开始std::cout << (cnt % 2);
}                      // 函数体结束

        函数声明可以在任何作用域中出现,但函数定义只能在命名空间作用域出现,或对于成员和友元函数,可以在类作用域中出现。在类体中声明而不带 friend 说明符的函数是类成员函数。这种函数拥有许多附加性质。

        每个函数都具有一个类型,它由函数的返回类型,所有形参的类型(形参列表),函数是否为 noexcept (C++17 起),以及对于非静态成员函数的 cv 限定性和引用限定性 (C++11 起)构成。函数类型同样拥有语言链接。不存在有 cv 限定的函数类型(不要与如 int f() const; 这样的 cv 限定函数类型,或如 std::string const f(); 这样的返回 cv 限定类型的函数相混淆)。如果有任何 cv 限定符被添加到到函数类型的别名,那么它会被忽略。

         1.2 函数指针

        函数不能按值传递或被其他函数所返回。可以有指向/到除主函数以外的所有函数 (C++20 前)所有非标准库函数和几个标准库函数 (C++20 起)的指针和引用,它们可以用于这些函数自身无法被使用的地方。因此我们说这些函数“可取址”。

void (*pf)() = &func;
pf();

        函数指针能以非成员函数或静态成员函数的地址初始化。由于存在函数到指针的隐式转换,取址运算符可以忽略。不同于函数或函数的引用,函数指针是对象,从而能存储于数组、被复制、被赋值等。

void f(int);
void (*p1)(int) = &f;
void (*p2)(int) = f; // 与 &f 相同

        更多关于函数指针说明见本专栏关于函数指针的博文:c/c++开发,无可避免的函数指针使用案例_py_free-物联智能的博客-CSDN博客

        1.3 函数对象

        函数调用表达式还支持函数指针以及重载了函数调用运算符及可转换为函数指针的任何类类型的值(包括 lambda 表达式) (C++11 起)。这些类型被统称为函数对象 (FunctionObject)。

        函数对象是指可以像函数一样被调用的对象,它可以重载函数调用运算符(),并且可以像函数一样接受参数和返回值。任何定义了函数调用操作符的对象都是函数对象。C++ 支持创建、操作新的函数对象,同时也提供了许多内置的函数对象。

        函数对象可以作为参数传递给函数,也可以作为返回值返回给调用者。函数对象可以用于实现一些特定的算法,例如排序、查找等。在c/c++标准库中,函数对象被广泛应用于算法和容器中,例如sort()函数中可以传递一个函数对象作为排序规则,或者在map容器中可以使用一个函数对象作为比较器来进行元素的排序和查找。此外,函数对象还可以用于实现自定义的操作符重载,例如可以定义一个函数对象来实现自定义的加法操作。

        函数对象和函数指针都可以作为函数的参数或返回值,但它们的实现方式不同。函数对象是一个类,它重载了函数调用运算符(),可以像函数一样被调用。而函数指针是一个指向函数的指针,可以通过指针调用函数。另外,函数对象可以保存状态,而函数指针不可以。

二、线程与函数

        线程是程序中的一条执行路径,通过执行多个线程,可以让程序在同一时间执行多个任务,提高程序的并发性和效率。

        函数指针是指指向函数的指针变量,可以将函数作为参数传递给其他函数,或者将函数作为返回值返回给调用者;函数对象是一个类,它重载了 () 运算符,可以像函数一样被调用。它们都是 c/c++语言中非常实用的特性,可以更好地实现复杂的功能。在多线程编程中,函数指针或函数对象可以作为线程的入口函数,让线程执行特定的任务。

        2.1 线程通过函数体入口获得真正任务执行语句

        下面来看一下线程与函数指针或函数对象结合,完成执行任务的例子:

//test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_void func_ptr_test(void);#endif //_TEST_1_H_
//test1.cpp
#include "test1.h"#include <thread>
#include <atomic>
#include <iostream>
#include <functional>std::atomic<int> cnt{0};
void func(){ cnt++;};
typedef void (*pfunc)();
using pfunc_us = void (*)();
//using pfunc_us=pfunc;void func_ptr_test(void)
{//函数引用std::thread t1{func}; // OKt1.join();std::cout << "cnt = " << cnt << "\n";//函数指针void (*pf)() = &func;std::thread t2{pf}; // OKt2.join();std::cout << "cnt = " << cnt << "\n";//函数指针别名pfunc pfc = &func;//= func;std::thread t3{pfc}; // OKt3.join();std::cout << "cnt = " << cnt << "\n";//函数指针别名pfunc_us pfus = &func;//= func;std::thread t4{pfus}; // OKt4.join();std::cout << "cnt = " << cnt << "\n";//函数对象,Lambda 表达式,闭包auto f = [&]{cnt++;};std::thread t5{f}; // OKt5.join();std::cout << "cnt = " << cnt << "\n";//函数包裹器std::function<void()> f_display = [&]{cnt++;};std::thread t6{f}; // OKt6.join();std::cout << "cnt = " << cnt << "\n";//函数包裹器std::function<void()> f_f = &func;std::thread t7{f_f}; // OKt7.join();std::cout << "cnt = " << cnt << "\n";//函数包裹器std::function<void()> f_fb = std::bind(func);std::thread t8{f_fb}; // OKt8.join();std::cout << "cnt = " << cnt << "\n";
};
//main.cpp
#include "test1.h"int main(int argc, char* argv[])
{func_ptr_test();return 0;
};

        在这个例子可以看到,通过函数引用、函数指针及函数指针别名、函数对象Lambda 表达式、函数对象包裹器等均可作为入口地址传递给线程类,线程运行是通过函数入口地址开始执行语句的。线程执行任务的真正实现内容是在传递进去函数体内完成。

        编译g++ main.cpp test*.cpp -o test.exe -std=c++11,运行程序:

        2.2 Lambda 表达式与函数

         在上述例子中,有两个很新颖的应用Lambda 表达式和std::function类模板。先来看Lambda 表达式,它能够捕获作用域中的变量的无名函数对象。

        标准库为其提供了一系列支持:

//语法支持
/*(1),完整声明,捕获,包含零或更多个捕获符的逗号分隔列表,可以默认捕获符(capture-default)起始 
*由说明符、 异常说明、 属性和尾随返回类型 按顺序组成,每个组分均非必需
* (C++20 起)向闭包类型的 operator() 添加约束 
*默认捕获符有&(以引用隐式捕获被使用的自动变量)和=(以复制隐式捕获被使用的自动变量)。
*/
[ 捕获 ] ( 形参 ) lambda说明符 约束(可选) { 函数体 }  /*(2),省略形参列表:函数不接收实参,如同形参列表是 ()*/
[ 捕获 ] { 函数体 }                                          //(C++23 前)
[ 捕获 ] lambda说明符 { 函数体 }                              //(C++23 起) /*(3),与(1) 相同,但指定泛型 lambda 并显式提供模板形参列表
*模板形参列表不能为空(不允许 <>)。*/            
[ 捕获 ] <模板形参> 约束(可选)( 形参 ) lambda说明符 约束(可选) { 函数体 }     //(C++20 起) /*(4),与 (2) 相同,但指定泛型 lambda 并显式提供模板形参列表。
*模板形参列表不能为空(不允许 <>)。*/
[ 捕获 ] <模板形参> 约束(可选) { 函数体 }                      // (C++20 起)(C++23 前) 
[ 捕获 ] <模板形参> 约束(可选) lambda说明符 { 函数体 }         //(C++23 起) 

        lambda 表达式是纯右值表达式,它的类型是独有的无名非联合非聚合类类型,被称为闭包类型(closure type),它(对于 ADL 而言)在含有该 lambda 表达式的最小块作用域、类作用域或命名空间作用域声明。

//闭包类型::operator()(形参)
返回类型 operator()(形参) const { 函数体 }  //(未使用关键词 mutable) 
返回类型 operator()(形参) { 函数体 }        //(使用了关键词 mutable) 
template<模板形参>返回类型 operator()(形参) const { 函数体 }  //(C++14 起)(泛型 lambda) 
template<模板形参>返回类型 operator()(形参) { 函数体 }        //(C++14 起)(泛型 lambda,使用了关键词 mutable) 

        前面例子,因为采用了auto关键字,它是一个泛型 lambda(当以 auto 为形参类型或显式提供模板形参列表 (C++20 起)时,该 lambda 为泛型 lambda)。它捕获([&])一个函数的引用,传入线程是,线程会获得函数体{cnt++;},即创建线程后,将执行cnt++;语句后,线程就完成了执行任务。

// 泛型 lambda,operator() 是无形参的模板
auto f = [&]{cnt++;};
//auto f = [&](void){cnt++;};
std::thread t5{f};

        更多的关于lambda 表达式的用法及格式要求不是本博文关注重点,就不在这里展开:

Lambda 捕获标识符:空、...、初始化器、&、&...、&初始化器、this、*this、...初始化器、&...初始化器
/*注,初始化器:(表达式)、=表达式、{表达式}*/
闭包类型::捕获
闭包类型::operator 返回类型(*)(形参)()
闭包类型::闭包类型()
闭包类型::operator=(const 闭包类型&)
闭包类型::~闭包类型()

        2.3 函数包装器-std::function类模板

        std::function类模板是定义在标准库头文件 <functional>,此头文件是函数对象库的一部分并提供标准散列函数。该头文件包含很多函数对象相关类模板及函数模板,本文就只讲述std::function和std::bind。


function    (C++11) 包装具有指定函数调用签名的任意可复制构造类型的可调用对象(类模板) 
bind        (C++11)  绑定一或多个实参到函数对象 

        类模板 std::function 是通用多态函数包装器。 std::function 的实例能存储、复制及调用任何可复制构造 (CopyConstructible) 的可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。

//std::function,定义于头文件 <functional>,(C++11 起) 
template< class >class function;                     
template< class R, class... Args > class function<R(Args...)>; 

        std::function 满足可复制构造 (CopyConstructible) 和可复制赋值 (CopyAssignable) 。类模板std::function的成员:

成员类型
类型                定义 
result_type         R 
argument_type       //(C++17 中弃用)(C++20 中移除) 若 sizeof...(Args)==1 且 T 是 Args... 中首个且唯一的类型,则为 T 
first_argument_type//(C++17 中弃用)(C++20 中移除) 若 sizeof...(Args)==2 且 T1 是 Args... 中二个类型的第一个,则为 T1 
second_argument_type//(C++17 中弃用)(C++20 中移除) 若 sizeof...(Args)==2 且 T2 是 Args... 中二个类型的第二个,则为 T2 成员函数
(构造函数)      构造新的 std::function 实例(公开成员函数) 
(析构函数)      析构 std::function 实例(公开成员函数) 
operator=      为内容赋值(公开成员函数) 
swap           交换内容(公开成员函数) 
assign         (C++17 中移除)  为内容赋值一个新的目标(公开成员函数) 
operator bool  检查是否包含了有效的目标(公开成员函数) 
operator()     调用其目标(公开成员函数) 目标访问
target_type    获得 std::function 所存储的目标的typeid(公开成员函数) 
target         获得指向 std::function 所存储的目标的指针(公开成员函数) 非成员函数
std::swap(std::function)    (C++11)特化 std::swap 算法(函数模板) 
operator==                   比较 std::function 和 nullptr(函数模板) 
operator!=                  (C++20 中移除)比较 std::function 和 nullptr(函数模板) 辅助类
std::uses_allocator<std::function>    (C++11) (C++17 前)特化std::uses_allocator类型特性(类模板特化) 

        类模板 std::function的声明定义:

namespace std {template<class> class function; // 不定义template<class R, class... ArgTypes>class function<R(ArgTypes...)> {public:using result_type = R;// 构造/复制/销毁function() noexcept;function(nullptr_t) noexcept;function(const function&);function(function&&) noexcept;template<class F> function(F);function& operator=(const function&);function& operator=(function&&);function& operator=(nullptr_t) noexcept;template<class F> function& operator=(F&&);template<class F> function& operator=(reference_wrapper<F>) noexcept;~function();// function 修改器void swap(function&) noexcept;// function 容量explicit operator bool() const noexcept;// function 调用R operator()(ArgTypes...) const;// function 目标访问const type_info& target_type() const noexcept;template<class T>       T* target() noexcept;template<class T> const T* target() const noexcept;};template<class R, class... ArgTypes>function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>;template<class F> function(F) -> function</* see description */>;// 空指针比较函数template<class R, class... ArgTypes>bool operator==(const function<R(ArgTypes...)>&, nullptr_t) noexcept;// 特化的算法template<class R, class... ArgTypes>void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
}

        从std::function类名板声明可以看出,其模板参数是由一个泛型返回类型R和多个泛型参数类型ArgTypes...组成,最终构成类似于lambda表达式的指向R(*)(ArgTypes...){};函数体。

三、线程与成员函数

         3.1 函数转发包装器- std::bind

        讲述std::function类模板时,涉及到一个重要的函数模板 std::bind ,它可以生成 函数f 的转发调用包装器。调用此包装器等价于以一些绑定到 args 的参数调用函数 f上。

/*std::bind,定义于头文件 <functional>
*参数:
*1)f - 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针) 
*2)args - 要绑定的参数列表,未绑定参数为命名空间 std::placeholders 的占位符 _1, _2, _3... 所替换 
*返回值:
*未指定类型 T 的函数对象,满足 std::is_bind_expression<T>::value == true 。
*/
template< class F, class... Args > 
/*unspecified*/ bind( F&& f, Args&&... args );  (C++11 起)(C++20 前) template< class F, class... Args > 
constexpr /*unspecified*/ bind( F&& f, Args&&... args );  (C++20 起) template< class R, class F, class... Args > 
/*unspecified*/ bind( F&& f, Args&&... args );  (C++11 起)(C++20 前) template< class R, class F, class... Args > 
constexpr /*unspecified*/ bind( F&& f, Args&&... args );  (C++20 起) 

        简单来说,函数模板 std::bind就是将函数(包括操作符)、成员函数(包括操作符)进行函数转换包装给调用者使用,相当于使用函数对象指针一样方便。调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与 std::unique_ptr),指向将访问其成员的对象。因此可以将std::bind指向的对象作为函数入口地址传递给线程,和将函数引用或指针传递是一样效果。

        3.2 std::bind与线程

//test2.h
#ifndef _TEST_2_H_
#define _TEST_2_H_void cfunc_ptr_test(void);#endif //_TEST_2_H_
//test2.cpp
#include "test2.h"#include <thread>
#include <iostream>
#include <functional>
#include <memory>struct ATest {void print1(int i) const { std::cout << "ATest::print1 i = " << i << '\n'; };
};struct CTest {CTest(int num) : num_(num) {};void operator()(int i) const{std::cout << "CTest::operator i = " << i << '\n';};void print_add(int i) const { std::cout << "CTest::print_add num_+i = " << num_+i << '\n'; };static void print2(int i){std::cout << "CTest::print2 2*i = " << 2*i << '\n';};struct BTest{void print1(int i) const { std::cout << "CTest::BTest::print1 i = " << i << '\n'; };};int num_;ATest at;BTest bt;
};void cfunc_ptr_test(void)
{//绑定到成员函数CTest ct(1);//f1类型:void (CTest::*(CTest*,int))(int i) constauto f1 = std::bind(&CTest::print_add, &ct, 10);//10为函数传入参数,&ct为传入地址std::thread t1{f1}; // OKt1.join();//绑定到成员函数,符号索引参数using std::placeholders::_1;auto f2 = std::bind(&CTest::print_add, &ct, _1);std::thread t2{f2,2 }; // OK,2为函数传入参数t2.join();//绑定到成员操作符auto f3 = std::bind(&ct, &ct, _1);  //CTest::operator()// auto f3 = std::bind(&CTest::operator(), &ct, _1); std::thread t3{f2,3 }; // OK,3为函数传入参数t3.join();//绑定智能指针指向的成员函数std::unique_ptr<CTest> pct(new CTest(1));auto f4 = std::bind(&CTest::print_add, &*pct.get(), _1);std::thread t4{f4,4 }; // OK,4为函数传入参数t4.join();//绑定智能指针指向的成员函数auto f5 = std::bind(&CTest::operator(), &*pct.get(), _1);//CTest::operator()std::thread t5{f5,5 }; // OK,5为函数传入参数t5.join();//静态成员函数作为线程入口// auto f6 = &CTest::print2;// std::thread t6{f6,6 }; // OK,6为函数传入参数std::thread t6{&CTest::print2,6 }; // OK,6为函数传入参数t6.join();//auto f7 = std::bind(&ATest::print1, &ct.at, _1);// f7(7);std::thread t7{f7,7 }; // OK,7为函数传入参数t7.join();//auto f8 = std::bind(&CTest::BTest::print1, &ct.bt, _1);// f8(8);std::thread t8{f8,8 }; // OK,8为函数传入参数t8.join();//绑定到成员函数,直接在bind内初始化一个对象传入auto f9 = std::bind(&CTest::print_add, new CTest(1), _1);//10为函数传入参数,&ct为传入地址std::thread t9{f9,9}; // OKt9.join();
};
//main.cpp
#include "test2.h"int main(int argc, char* argv[])
{cfunc_ptr_test();return 0;
};

        另外,标准库还有和std::bind相差不大的函数转发调用转发调用包装:std::bind_front,这是C++20的标准。调用此包装等价于绑定首 sizeof...(Args) 个参数到 args 再调用函数 f,这里就不细说了,有兴趣的自行查看C++20资料。

        编译g++ main.cpp test2.cpp -o test.exe -std=c++11,运行生产程序:

相关文章:

C/C++开发,无可避免的多线程(篇四).线程与函数的奇妙碰撞

一、函数、函数指针及函数对象 1.1 函数 函数&#xff08;function&#xff09;是把一个语句序列&#xff08;函数体, function body&#xff09;关联到一个名字和零或更多个函数形参&#xff08;function parameter&#xff09;的列表的 C 实体&#xff0c;可以通过返回或者抛…...

elisp简单实例: taglist

从vim 转到emacs 下,一直为缺少vim 中的tablist 插件而感到失落. 从网上得到的一个emacs中的taglist, 它的功能很简陋,而且没有任何说明, 把它做为elisp的简单实例,供初学者入门倒不错,我给它加了很多注释,帮助理解, 说实话,感觉这百行代码还是挺有深度的,慢慢体会,调试才会有收…...

Azure AI基础到实战(C#2022)-认知服务(3)

目录 OpenFileDialog 类上一节代码的API剖析ComputerVisionClientExtensions.ReadAsync MethodReadHeaders ClassReadHeaders.OperationLocation Property探索ReadHeaders加上调试代码可用于 Azure 认知服务的身份验证标头使用单服务订阅密钥进行身份验证使用多服务订阅密钥进行…...

aws apigateway 使用restapi集成lambda

参考资料 代理集成&#xff0c;https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html非代理集成&#xff0c;https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/getting-started-…...

HTML基础

HTML 基础 文章目录HTML 基础列表标签无序列表有序列表自定义列表表格标签表格基本标签表格基本结构表格完整结构&#xff1a;合并行和合并列表单标签input 系列标签属性标签text 标签radio 标签 单选框file 标签 文件选择button 标签 按钮input系列标签总结button按钮标签sele…...

ThreadPoolExecutor参数 keepAliveTime allowCoreThreadTimeOut

/*** Timeout in nanoseconds for idle threads waiting for work.* Threads use this timeout when there are more than corePoolSize* present or if allowCoreThreadTimeOut. Otherwise they wait* forever for new work.*/ private volatile long keepAliveTime;等待工作的…...

什么是Hibernate框架?

简单介绍&#xff1a;Hibernate框架是当今主流的java持久层框架之一&#xff0c;是一个开放源码的ORM&#xff08;Object Relational Mapping&#xff0c;对象关系映射&#xff09;框架&#xff0c;它对JDBC进行了轻量级的封装&#xff0c;使得JAVA开发人员可以使用面向对象的编…...

指针面试笔试题练习

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍c语言中有关指针更深层的知识. 金句分享: ✨星光…...

docker(三)仓库的搭建、官方私有仓库的加密和认证

文章目录一、docker仓库二、仓库Registry工作原理三、搭建本地私有仓库四、配置镜像加速器五、私有仓库的加密认证1.非加密下上传拉取2.insecure registry3.仓库加密4.仓库认证一、docker仓库 什么是仓库 Docker 仓库是用来包含镜像的位置&#xff0c;Docker提供一个注册服务器…...

FPGA实现SDI视频编解码 SDI接收发送,提供2套工程源码和技术支持

目录1、前言2、设计思路和框架SDI接收SDI缓存写方式处理SDI缓存读方式处理SDI缓存的目的SDI发送3、工程1详解4、工程2详解5、上板调试验证并演示6、福利&#xff1a;工程代码的获取1、前言 FPGA实现SDI视频编解码目前有两种方案&#xff1a; 一是使用专用编解码芯片&#xff0…...

Android 基础知识4-3.5 RadioButton(单选按钮)Checkbox(复选框)详解

一、RadioButton&#xff08;单选按钮&#xff09; 1.1、简介 RadioButton表示单选按钮&#xff0c;是button的子类&#xff0c;每一个按钮都有选择和未选中两种状态&#xff0c;经常与RadioGroup一起使用&#xff0c;否则不能实现其单选功能。RadioGroup继承自LinearLayout&a…...

用代码实现解析解的方式求解_梯度下降法思路_导函数有什么用_接23节---人工智能工作笔记0026

这里24节,25节,介绍了一下人工智能高等数学要学习的一些内容,初步了解了一下,微积分中用到的知识~微分~以及导数这里... 然后接着23节,我们还是继续,走人工智能的主线,先把整体的人工智能的内容学习一遍,然后再去回去看数学知识更有目的性. 然后首先来回顾一下,这里机器学习,其…...

大数据ETL开发之图解Kettle工具

详细笔记参考&#xff1a;https://blog.csdn.net/yuan2019035055/article/details/120409547以下只是简单记录一下我学习过程中的心得3.1.5 JSON输入JSONPath 类似于 XPath 在 xml 文档中的定位&#xff0c;JsonPath 表达式通常是用来路径检索或设置Json的。其表达式可以接受“…...

docker-容器数据卷

Docker挂载主机目录访问如果出现cannot open directory.:Permission denied 解决办法:在挂载目录后多加一个--privileged=true参数即可; 一、介绍 卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能绕过Union Fil…...

【C++】类和对象补充知识点

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、再谈构造函数1.1 构造函数体赋…...

路径规划-人工势场法

一.基本思想 目标点对机器人产生吸引力&#xff0c;障碍物对机器人产生排斥力&#xff1b; 所有力的合成构成机器人的控制律 二. 主要步骤 1.构建人工势场 目标点&#xff1a;吸引势场 障碍物&#xff1a;排斥势场 2.根据人工势场计算力 对势场求偏导 3.计算合力 计…...

20230304学习笔记

1、Mybatis #{}和${}的区别是什么 a、#{}是预编辑处理、是占位符&#xff0c;${}是字符串拼接符。 b、#{}替换为&#xff1f;号&#xff0c;用PreparedStatement来赋值&#xff0c;${}直接替换变量的值&#xff0c;用Statement赋值。 c、#{}在DBMS中、自动加入单引号&#…...

[数据集][VOC][目标检测]河道垃圾水面漂浮物数据集目标检测可用yolo训练-1304张介绍

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;1304 标注数量(xml文件个数)&#xff1a;1304 标注类别数&#xff1a;1 标注类别名称:["trash"] …...

JavaWeb--JSP案例

JSP案例8 案例8.1 环境准备8.1.1 创建工程8.1.2 创建包8.1.3 创建表8.1.4 创建实体类8.1.5 准备mybatis环境8.2 查询所有8.2.1 编写BrandMapper8.2.2 编写工具类8.2.3 编写BrandService8.2.4 编写Servlet8.2.5 编写brand.jsp页面8.2.6 测试8.3 添加8.3.1 编写BrandMapper方法8.…...

推荐系统1--Deepfm学习笔记

目录 1 keras实现Deepfm demo 2 deepctr模版 3 其他实现方式 ctr_Kera 模型 数据集 预处理 执行步骤 4何为focal loss 参考 1 keras实现Deepfm 假设我们有两种 field 的特征&#xff0c;连续型和离散型&#xff0c;连续型 field 一般不做处理沿用原值&#xff0c;离散型一…...

javaDoc生成方式

javaDoc生成方式 命令行生成 在cmd控制台窗口上找到需要生成文件的路径&#xff0c;然后执行命令。 # javadoc -encoding UTF-8 -charset UTF-8 文件名 javadoc -encoding UTF-8 -charset UTF-8 Doc.java这样就生成完成了。 测试Doc.java文件 package com.jiang.base;/***…...

Armv9 registers

A64指令集包括访问system registers编码空间的指令。这些指令有&#xff1a; Access to System registers, including the debug registers, that provide system control, and system status information.Access to Special-purpose registers such as SPSR_ELx, ELR_ELx, an…...

套接字实现TCP

套接字 套接字的意义就是客户端与服务器进行双向通信的端点&#xff0c;如果有不理解点上面套接字三字更近距离了解套接字。 网络套接字与客户连接的特定网络有关的服务端口号&#xff0c;这个端口号允许linux进入特定的端口号的连接转到正确的服务器进程。 套接字通信的建立过…...

MMSeg绘制模型指定层的Heatmap热力图

文章首发及后续更新&#xff1a;https://mwhls.top/4475.html&#xff0c;无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评&#xff0c;非常感谢&#xff01; 摘要&#xff1a;绘制模型指定层的热力图 可视化环境安装 …...

关于Paul C.R. - Inductance_ Loop and partial-Wiley (2009)一书的概括

环感抗和部分感抗是两种不同的电路元件&#xff0c;它们通常用于描述不同类型的导体结构中的电流承载能力。 环感抗通常用于描述绕制在磁性芯上的线圈。当电流通过线圈时&#xff0c;它会在磁性芯中产生一个磁场&#xff0c;这个磁场又会对线圈产生一个磁通量。这个磁通量的大…...

基于支持向量机SVM的面部表情分类预测

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 SVM应用实例,基于SVM的面部表情分类预测 代码 结果分析 展望 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本…...

java内存模型的理解

java内存模型的理解并发问题产生的源头缓存导致的可见性问题线程切换导致的原子性问题编译优化带来的有序性问题小结Java内存模型: 解决可见性和有序性问题Java内存模型与JVM内存模型的区别volatile关键字Happens-Before规则小结思考题参考并发问题产生的源头 缓存导致的可见性…...

自己写一个简单的IOC

什么是SpringIOC&#xff1f; 答&#xff1a;IOC即控制反转&#xff0c;就是我们不在手动的去new一个对象&#xff0c;而是将创建对象的权力交给Spring去管理&#xff0c;我们想要一个User类型的对象&#xff0c;就只需要定义一个User类型的变量user1&#xff0c;然后让Spring去…...

用Python批量重命名文件

案例 今天,我们来整理文件夹中的文件,给某个文件夹下的所有文件重新命名。要求是给所有文件按照修改时间,按顺序在文件名前面加上编号。比如将文件资料.xlsx重命名为1. 资料.xlsx import osdef Get_modify_time(file):return os.path.getmtime(file) #获取文件修改时间path…...

iis之web服务器搭建、部署(详细)~千锋

目录 Web服务器 部署web服务器 实验一 发布一个静态网站 实验二 一台服务器同时发布多个web站点 网站类型 Web服务器 也叫网页服务或HTTP服务器web服务器使用的协议是HTTPHTTP协议端口号&#xff1a;TCP 80、HTTPS协议端口号&#xff1a;TCP 443Web服务器发布软件&…...