CMake基础【学习笔记(八)】

声明此博客为转载

CMake基础

文章目录

  • CMake基础
    • 一、准备知识
      • 1.1 C++的编译过程
      • 1.2 静态链接库和动态链接库
      • 1.3 为什么需要CMake
        • 1.3.1 g++ 命令行编译
        • 1.3.2 CMake简介
    • 二、CMake基础知识
      • 2.1 安装
      • 2.2 第一个CMake例子
      • 2.3 语法基础
        • 2.3.1 指定版本
        • 2.3.2 设置项目
        • 2.3.3 添加可执行文件目标
        • 2.3.4 生成静态库并链接
        • 2.3.5 生成动态库并连接
        • 2.3.6 CMake 中的 PUBLIC、PRIVATE、INTERFACE
        • 2.3.7 变量
        • 2.3.8 include引入其他代码
        • 2.3.9 条件控制
        • 2.3.10 CMake分步编译
        • 2.3.11 生成器表达式
        • 2.3.12 函数和宏
        • 2.3.13 设置安装
        • 2.3.14 寻找依赖 find_package
    • 三、opencv CMake示例

一、准备知识

1.1 C++的编译过程

使用g++等编译工具,从源码生成最终的可执行文件一般有这几步:预处理(Preprocess)、编译(Compile)、汇编(assemble)、链接(link)。

输入g++ --help可以看到对应命令:

-E                       Preprocess only; do not compile, assemble or link.
-S                       Compile only; do not assemble or link.
-c                       Compile and assemble, but do not link.
-o <file>                Place the output into <file>.

以下面程序为例:

#include <iostream>int main() {std::cout << "Hello World!" << std::endl;return 0;
}
  • 第一步:预处理
    C++中预处理指令以 # 开头。在预处理阶段,会对#define进行宏展开,处理#if,#else等条件编译指令,递归处理#include。这一步需要我们添加所有头文件的引用路径。

    # 将xx.cpp源文件预处理成xx.i文件(文本文件)
    g++ -E main.cpp -o main.i
    
  • 第二步:编译

    检查代码的规范性和语法错误等,检查完毕后把代码翻译成汇编语言文件。

    # 将xx.i文件编译为xx.s的汇编文件(文本文件)
    g++ -S main.i -o main.s
    
  • 第三步:汇编
    基于汇编语言文件生成二进制格式的目标文件。

    # 将xx.s文件汇编成xx.o的二进制目标文件
    g++ -c main.s -o main.o
    
  • 第四步:链接

    将目标代码与所依赖的库文件进行关联或者组装,合成一个可执行文件

    # 将xx.o二进制文件进行链接,最终生成可执行程序
    g++ main.o -o main
    

1.2 静态链接库和动态链接库

所谓静态和动态,其区别是链接的阶段不一样。

  • 静态链接库名称一般是lib库名称.a.a代表archive library),其链接发生在编译环节。一个工程如果依赖一个静态链接库,其输出的库或可执行文件会将静态链接库*.a打包到该工程的输出文件中(可执行文件或库),因此生成的文件比较大,但在运行时也就不再需要库文件了。

  • 而动态链接库的链接发生在程序的执行过程中,其在编译环节仅执行链接检查,而不进行真正的链接,这样可以节省系统的开销。动态库一般后缀名为*.so.so代表shared object,Linux:lib库名称.so ,macOS:lib库名称.dylib)。动态链接库加载后,在内存中仅保存一份拷贝,多个程序依赖它时,不会重复加载和拷贝,这样也节省了内存的空间。

  • 以下图为例

    • 工程AB依赖静态链接库 static libraryAB在运行时,内存中会有多份static library

    • 工程AB依赖动态链接库 shared libraryAB在运行时,内存中只有一份 shared library(shared:共享)。

以上只是非常简单的一个解释以区分动态链接库和静态链接库。更多底层的知识需要单独进行深入讲解。

1.3 为什么需要CMake

1.3.1 g++ 命令行编译

当我们编译附件中1.hello_world时,我们可以运行

g++ main.cpp -o main

当我们需要引入外部库时,如附件中的2.external_libs,需要引入gflags(Google开源的命令行参数处理库),我们则需要运行:

# 安装gflags
sudo apt-get install libgflags-dev libgflags2.2 // -lgflags表示链接gflags库,-o main表示输出文件名为main
g++ main.cpp -lgflags -o main # 或者:# 安装pkg-config
sudo apt-get install pkg-config// pkg-config是一个工具,用于查找和管理安装在系统上的库文件,--cflags --libs gflags表示查找gflags库的头文件和库文件的路径,-o main表示输出文件名为maing++ main.cpp `pkg-config --cflags --libs gflags`  -o main # 测试输出
./main --age 31 --name alice

有些时候有一些常用库我们也不用手动添加头文件或链接库路径,通常g++能在默认查询路径中找到他们。当我们的项目文件变得多起来,引入的外部库也多起来时,命令行编译这种方式就会变得十分臃肿,也不方便调试和编辑。通常在测试单个文件时会使用命令行进行编译,但不推荐在一个实际项目中使用命令行编译。

1.3.2 CMake简介

在实际工作中推荐使用CMake构建C++项目,CMake是用于构建、测试和软件打包的开源跨平台工具;

特性:

  • 自动搜索可能需要的程序、库和头文件的能力;
  • 独立的构建目录(如build),可以安全清理
  • 支持复杂的自定义命令(下载、生成各种文件)
  • 自定义配置可选组件
  • 从简单的文本文件(CMakeLists.txt)自动生成工作区和项目的能力
  • 在主流平台上自动生成文件依赖项并支持并行构建
  • 几乎支持所有的IDE

二、CMake基础知识

2.1 安装

ubuntu上请执行

sudo apt install cmake -y

或者编译安装:

# 以v3.25.1版本为例
git clone -b v3.25.1 https://github.com/Kitware/CMake.git 
cd CMake
# 你使用`--prefix`来指定安装路径,或者去掉`--prefix`,安装在默认路径。
./bootstrap --prefix=<安装路径> && make && sudo make install# 验证
cmake --version

2.2 第一个CMake例子

附件位置:3.first_cmake

# 第一步:配置,-S 指定源码目录,-B 指定构建目录
cmake -S . -B build 
# 第二步:生成,--build 指定构建目录
cmake --build build
# 运行
./build/first_cmake

vs code插件:

  • 安装twxs.cmake做代码提示;
  • 安装ms-vscode.cmake-tools界面操作。

2.3 语法基础

2.3.1 指定版本

以附件:3.first_cmake/CMakeLists.txt为例:

# CMake 最低版本号要求
cmake_minimum_required(VERSION 3.10)# first_cmake是项目名称,VERSION是版本号,DESCRIPTION是项目描述,LANGUAGES是项目语言
project(first_cmake VERSION 1.0.0 DESCRIPTION "项目描述"LANGUAGES CXX) # 添加一个可执行程序,first_cmake是可执行程序名称,main.cpp是源文件
add_executable(first_cmake main.cpp)

命令cmake_minimum_required来指定当前工程所使用的CMake版本,不区分大小写的,通常用小写。VERSION是这个函数的一个特殊关键字,版本的值在关键字之后。CMake中的命令大多和cmake_minimum_required相似,不区分大小写,并有很多关键字来引导命令的参数输入(类似函数传参)。

2.3.2 设置项目

以附件:3.first_cmake/CMakeLists.txt为例:

project(ProjectName VERSION 1.0.0 DESCRIPTION "项目描述"LANGUAGES CXX) 

CMakeLists.txt的开头,都会使用project来指定本项目的名称、版本、介绍、与使用的语言。在project中,第一个ProjectName(例子中用的是first_cmake)不需要参数,其他关键字都有参数。

2.3.3 添加可执行文件目标

以附件:3.first_cmake/CMakeLists.txt为例:

add_executable(first_cmake main.cpp)

这里我们用到add_executable,其中第一个参数是最终生成的可执行文件名以及在CMake中定义的Target名。我们可以在CMake中继续使用Target的名字为Target的编译设置新的属性和行为。命令中第一个参数后面的参数都是编译目标所使用到的源文件。

2.3.4 生成静态库并链接

附件位置:4.static_lib_test

A.生成静态库

#account_dir/CMakeLists.txt# 最低版本要求
cmake_minimum_required(VERSION 3.10)# 项目信息
project(Account)# 添加静态库,Linux下会生成libAccount.a
add_library(Account STATIC Account.cpp Account.h)
# 编译静态库后,会在build下生成 build/libAccount.a 静态库文件
account_dir/
├── Account.cpp
├── Account.h
├── build
│   └── libAccount.a
└── CMakeLists.txt

这里我们用到add_library, 和add_executable一样,Account为最终生成的库文件名(lib库名称.a),第二个参数是用于指定链接库为动态链接库(SHARED)还是静态链接库(STATIC),后面的参数是需要用到的源文件。

B.链接

# test_account/CMakeLists.txt# 最低版本要求
cmake_minimum_required(VERSION 3.10)# 项目名称
project(test_account)# 添加执行文件
add_executable(test_account test_account.cpp)# 添加头文件目录,如果不添加,找不到头文件
target_include_directories(test_account PUBLIC "../account_dir")
# 添加库文件目录,如果不添加,找不到库文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 添加目标链接库
target_link_libraries(test_account PRIVATE Account)
# 编译后目录如下
4.static_lib_test/
├── account_dir
│   ├── Account.cpp
│   ├── Account.h
│   ├── build
│   │   └── libAccount.a
│   └── CMakeLists.txt
└── test_account ├── build│   └── test_account├── CMakeLists.txt└── test_account.cpp

我们通过add_libraryadd_executable定义了Target,我们可以通过Target的名称为其添加属性,例如:

# 指定目标包含的头文件目录
target_include_directories(test_account PUBLIC "../account_dir")
# 添加库文件目录,如果不添加,找不到库文件
target_link_directories(test_account PUBLIC "../account_dir/build")
# 指定目标链接的库
target_link_libraries(test_account PRIVATE Account)
  • 通过target_include_directories,我们给test_account添加了头文件引用路径"../account_dir"。上面的关键词PUBLIC,PRIVATE用于说明目标属性的作用范围,更多介绍参考下节。
  • 通过target_link_libraries,将前面生成的静态库libAccount.a链接给对象test_account,但此时还没指定库文件的目录,CMake无法定位库文件
  • 再通过target_link_directories,添加库文件的目录即可。
2.3.5 生成动态库并连接

附件位置:5.dynamic_lib_test

A.生成动态库

#account_dir/CMakeLists.txt# 添加动态库,Linux下会生成libAccount.so
add_library(Account SHARED Account.cpp Account.h)
# 编译动态库后,会在build下生成 build/libAccount.so 动态库文件
account_dir/
├── Account.cpp
├── Account.h
├── build
│   └── libAccount.so
└── CMakeLists.txt

B.链接

操作不变。

# ldd查看依赖的动态库
libAccount.so => /home/enpei/Documents/course_cpp_tensorrt/course_5/src/5.dynamic_lib_test/test_account/../account_dir/build/libAccount.so (0x00007fb692cf1000)

当然,也可以用一个CMakeLists.txt来一次性编译,参考附件6.build_together

#6.build_together/CMakeLists.txt`# 最低版本要求
cmake_minimum_required(VERSION 3.10)# 项目信息
project(test_account)# 添加动态库
add_library(Account SHARED "./account_dir/Account.cpp" "./account_dir/Account.h")# 添加可执行文件
add_executable(test_account "./test_account/test_account.cpp")# 添加头文件
target_include_directories(test_account PUBLIC "./account_dir")
# 添加链接库
target_link_libraries(test_account Account)
2.3.6 CMake 中的 PUBLIC、PRIVATE、INTERFACE

CMake中经常使用target_...()类似的命令,一般这样的命令支持通过PUBLICPRIVATEINTERFACE关键字来控制传播。

target_link_libraries(A B)为例,从理解的角度来看

  • PRIVATE :依赖项B仅链接到目标 A,如果有C 链接了AC不会链接B
  • INTERFACE :依赖项B并不链接到目标A,如果有C 链接了AC会链接B
  • PUBLIC :依赖项B链接到目标 A,如果有C 链接了AC也会链接B

其实就是对象属性的传递,打个散烟的比方:

  • PRIVATE: 就是自己抽,不给别人抽
  • INTERFACE :就是自己不抽,给别人抽
  • PUBLIC :就是自己抽,也给别人抽

从使用的角度来说,如果有C链接了目标A

  • 如果B仅用于A的实现,且不在头文件中提供给C使用,使用PRIVATE
  • 如果B不用于A的实现,仅在头文件中作为借口给C使用,使用INTERFACE
  • 如果B既用于A的实现,也在头文件中提供给C使用,使用PUBLIC

举例:

# 创建库
add_library(C c.cpp)
add_library(D d.cpp)
add_library(B b.cpp)# C是B的PUBLIC依赖项
target_link_libraries(B PUBLIC C)
# D是B的PRIVATE依赖项
target_link_libraries(B PRIVATE D)# 添加可执行文件
add_executable(A a.cpp)# 将B链接到A
target_link_libraries(A B)
  • 因为CBPUBLIC依赖项,所以C会传播到A
  • 因为DBPRIVATE依赖性,所以D不会传播到A
2.3.7 变量

附件位置:7.message_var_demo

像其他编程语言一样,我们应该将CMake理解为一门编程语言。我们也需要设定变量来储存我们的选项,信息。有时候我们通过变量来判断我们在什么平台上,通过变量来判断我们需要编译哪些Target,也通过变量来决定添加哪些依赖。

2.3.8 include引入其他代码

附件位置:8.include_demo

2.3.9 条件控制

附件位置:9.if_demo

正如前面所讲,应该把CMake当成编程语言,除了可以设置变量以外,CMake还可以写条件控制。

if(variable)# 为true的常量:ON、YES、TRUE、Y、1、非0数字
else()# 为false的常量:OFF、NO、FALSE、N、0、空字符串、NOTFOUND
endif()

可以和条件一起使用的关键词有

NOT, TARGET, EXISTS (file), DEFINED等
STREQUAL, AND, OR, MATCHES (regular expression), VERSION_LESS, VERSION_LESS_EQUAL等
2.3.10 CMake分步编译

附件位置:10.steps_demo

# 查看所有目标
$ cmake -S . -B build
$ cd build
$ cmake --build . --target helpThe following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... rebuild_cache
... edit_cache
... steps_demo
... main.o
... main.i
... main.s# 1.预处理
$ cmake --build . --target main.i
# 输出:Preprocessing CXX source to CMakeFiles/steps_demo.dir/main.cpp.i
# 可以打开滑到底部# 2.编译
$ cmake --build . --target main.s
# 输出汇编代码:Compiling CXX source to assembly CMakeFiles/steps_demo.dir/main.cpp.s# 3.汇编
$ cmake --build . --target main.o
# 输出二进制文件:Building CXX object CMakeFiles/steps_demo.dir/main.cpp.o# 链接
$ cmake --build .
Scanning dependencies of target steps_demo
[ 50%] Linking CXX executable steps_demo
[100%] Built target steps_demo# 运行
./steps_demo
2.3.11 生成器表达式

附件位置:11.generator_expression

生成器表达式简单来说就是在CMake生成构建系统的时候根据不同配置动态生成特定的内容。有时用它可以让代码更加精简,我们介绍几种常用的。

需要注意的是,生成表达式被展开是在生成构建系统的时候,所以不能通过解析配置CMakeLists.txt阶段的message命令打印,可以用类似file(GENERATE OUTPUT "./generator_test.txt" CONTENT "$<$<BOOL:TRUE>:TEST>")生成文件的方式间接测试。

在其最一般的形式中,生成器表达式是$<...>,尖括号中间可以是如下几种类型:

  • 条件表达式
  • 变量查询(Variable-Query)
  • 目标查询(Target-Query)
  • 输出相关的表达式
# 1.条件表达式:$<condition:true_string>,当condition为真时,返回true_string,否则返回空字符串
$<0:TEST>  
$<1:TEST>  
$<$<BOOL:TRUE>:TEST># 2.变量查询(Variable-Query)
$<TARGET_EXISTS:target>:判断目标是否存在
$<CONFIG:Debug>:判断当前构建类型是否为Debug# 3.目标查询(Target-Query)
$<TARGET_FILE:target>:获取编译目标的文件路径
$<TARGET_FILE_NAME:target>:获取编译目标的文件名

4.输出相关表达式:用于在不同的环节使用不同参数,比如需要在installbuild环节分别用不同的参数,我们可以这样写:

add_library(Foo ...)
target_include_directories(FooPUBLIC$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)

其中$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>仅在build环节生效;而$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>仅在install环节生效。通过设定不同阶段不同的参数,我们可以避免路径混乱的问题。

2.3.12 函数和宏

附件位置:12.function_macro

# 定义一个宏,宏名为my_macro,没有参数
macro(my_macro)message("宏内部的信息")set(macro_var "宏内部变量test")
endmacro(my_macro)# 定义一个函数,函数名为second_func,有两个参数
function(second_func arg1 arg2)message("第一个参数:${arg1}, 第二个参数:${arg2}")
endfunction(second_func)
2.3.13 设置安装

附件位置:13.install_demo

当需要发布项目时你需要指定项目文件的安装路径。下面的代码片段中,使用install安装demo_test,并分别将可执行文件安装在bin中,动态链接库和静态链接库都安装在lib,公共头文件安装在include。这里的路径都将添加${CMAKE_INSTALL_PREFIX}作为前缀(如果不设置CMAKE_INSTALL_PREFIX,则会安装到/usr/local 目录下)。实现安装的功能在你需要发布你项目给其他人使用时,非常有用。

# 设置安装
install(TARGETS demo_testRUNTIME DESTINATION bin # 可执行文件LIBRARY DESTINATION lib # 动态库ARCHIVE DESTINATION lib # 静态库PUBLIC_HEADER DESTINATION include # 公共头文件
)
2.3.14 寻找依赖 find_package

对于大部分支持了CMake的项目来说,均可以通过find_package找到对应的依赖库,参考附件:14.find_demo

# 使用find_package寻找<LibaryName>库,如果找到,一般都会有以下变量(库作者设置)
<LibaryName>_FOUND:表示是否找到
<LibaryName>_INCLUDE_DIR:表示头文件目录
<LibaryName>_LIBRARIES:表示库文件目录

假设我们编写了一个新的函数库,我们希望别的项目可以通过find_package对它进行引用,我们有两种办法:

  • 编写一个Find<LibraryName>.cmake,适用于导入非cmake安装的项目,参考附件:15.custom_find
  • 使用install安装,生成<LibraryName>Config.cmake文件,适用于导入自己开发的cmake项目,参考附件:16.custom_install_demo

三、opencv CMake示例

附件位置:17.demo_opencv/

安装OpenCV:sudo apt install libopencv-dev

依赖和链接OpenCV与常规的添加依赖并没有太多不同,同时OpenCV提供了cmake find package的功能,因此我们可以通过find_package方便的定位opencv在系统中的位置和需要添加的依赖。

find_package(OpenCV REQUIRED)message("OPENCV INCLUDE DIRS: ${OpenCV_INCLUDE_DIRS}")
message("OPENCV LINK LIBRARIES: ${OpenCV_LIBS}")

如果cmake找到了OpenCV,配置cmake后,命令行会有如下输出:

OPENCV INCLUDE DIRS: /usr/include/opencv4
OPENCV LINK LIBRARIES: opencv_calib3d;opencv_core;opencv_dnn;opencv_features2d;opencv_flann;opencv_highgui;opencv_imgcodecs;opencv_imgproc;opencv_ml;opencv_objdetect;opencv_photo;opencv_stitching;opencv_video;opencv_videoio;opencv_aruco;opencv_bgsegm;opencv_bioinspired;opencv_ccalib;opencv_datasets;opencv_dnn_objdetect;opencv_dnn_superres;opencv_dpm;opencv_face;opencv_freetype;opencv_fuzzy;opencv_hdf;opencv_hfs;opencv_img_hash;opencv_line_descriptor;opencv_optflow;opencv_phase_unwrapping;opencv_plot;opencv_quality;opencv_reg;opencv_rgbd;opencv_saliency;opencv_shape;opencv_stereo;opencv_structured_light;opencv_superres;opencv_surface_matching;opencv_text;opencv_tracking;opencv_videostab;opencv_viz;opencv_ximgproc;opencv_xobjdetect;opencv_xphoto

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

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

相关文章

Goby 漏洞发布| Cisco IOS XE ebui_wsma_http 接口权限绕过漏洞(CVE-2023-20198)

漏洞名称&#xff1a; Cisco IOS XE ebui_wsma_http 接口权限绕过漏洞&#xff08;CVE-2023-20198&#xff09; English Name&#xff1a; Cisco IOS XE ebui_wsma_http API Permission Bypass Vulnerability (CVE-2023-20198) CVSS core: 10 影响资产数&#xff1a; 307282…

kafka为什么如此之快?

天下武功&#xff0c;唯快不破。同样的&#xff0c;kafka在消息队列领域&#xff0c;也是非常快的&#xff0c;这里的块指的是kafka在单位时间搬运的数据量大小&#xff0c;也就是吞吐量&#xff0c;下图是搬运网上的一个性能测试结果&#xff0c;在同步发送场景下&#xff0c;…

以八数码问题为例实现A*算法的求解(未完结)

八数码&#xff1a; 在一个 33 的网格中&#xff0c;1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 33 的网格中。 例如&#xff1a; 1 2 3 x 4 6 7 5 8在游戏过程中&#xff0c;可以把 x 与其上、下、左、右四个方向之一的数字交换&#xff08;如果存在&#xff09;。…

(11月4日)GBASE南大通用 x openGauss Meetup,欢迎报名

由openGauss社区、天津南大通用数据技术股份有限公司主办&#xff0c;伟仕佳杰科技有限公司、神州数码&#xff08;中国&#xff09;有限公司协办的“GBASE南大通用 x openGauss Meetup”活动将于2023年11月4日&#xff08;周六&#xff09;在合肥市高新区云飞路66号天源迪科科…

3DEXPERIENCE云端项目管理小工具--Project Planner项目策划者角色

云端3DEXPERIENCE平台提供了一个协作环境&#xff0c;使企业和个人能够以全新的方式实现创新。它将人员、创意、数据和解决方案连接到一个始终在线且可用的协作和交互环境中&#xff0c;可以帮助您的企业提高执行力、生产率并加速创新。 3DEXPERIENCE中的Project Planner项目策…

BUUCTF 后门查杀 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 小白的网站被小黑攻击了&#xff0c;并且上传了Webshell&#xff0c;你能帮小白找到这个后门么&#xff1f;(Webshell中的密码(md5)即为答案)。 密文&#xff1a; 下载附件&#xff0c;解压得到一个网站文件夹。 解…

Nginx搭载负载均衡及前端项目部署

目录 ​编辑 一.Nginx安装 1.安装所需依赖 2.下载并解压Nginx安装包 3.安装nginx 4.启动Nginx服务 二.Tomcat负载均衡 1.准备环境 1.1 准备两个Tomcat 1.2 修改端口号 1.3 配置Nginx服务器集群 2.效果展示 ​编辑三.前端项目打包 ​编辑四.前端项目部署 1.上传项目…

YOLO目标检测——安全帽佩戴检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;安全帽佩戴检测数据集可以用于实时检测工作人员是否按照要求佩戴了安全帽&#xff0c;以保障他们的安全数据集说明&#xff1a;安全帽佩戴检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;图片分为带头盔和没带头…

智慧矿山:AI算法在带式运输机中的异物识别应用

随着现代农业和工业的发展&#xff0c;带式运输机在各种生产作业中发挥着越来越重要的作用。然而&#xff0c;在带式运输机运行过程中&#xff0c;可能会混入各种异物&#xff0c;这些异物的存在可能会对运输过程和设备本身造成损害。为了解决这一问题&#xff0c;本文将介绍一…

c++装饰器模式

前言 装饰器模式&#xff0c;就是可以对一个对象无限装饰一些东西&#xff0c;而且可以没有顺序。比如一个人可能只会说出他的名字&#xff0c;但是可以让他再说哈哈&#xff0c;可以说完哈哈之后再说哇哇。如何后面又不想装饰了&#xff0c;不需要改类原来的代码&#xff0c;…

基于深度学习的人脸性别年龄识别 - 图像识别 opencv 计算机竞赛

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

智慧校园地下管线三维可视化管控平台减少人力和物力资源的浪费

随着科技的不断发展&#xff0c;三维可视化管理平台在各个领域得到了广泛的应用。三维可视化管理平台通过将数据以三维形式呈现&#xff0c;使得用户能够更直观地理解和分析数据&#xff0c;从而提高工作效率和决策质量。 VR数字孪生园区系统是通过将实际园区的各种数据和信息进…

RabbitMQ入门到实战教程,消息队列实战,改造配置MQ

RabbitMQ入门到实战教程&#xff0c;MQ消息中间件&#xff0c;消息队列实战-CSDN博客 3.7.Topic交换机 3.7.1.说明 Topic类型的Exchange与Direct相比&#xff0c;都是可以根据RoutingKey把消息路由到不同的队列。 只不过Topic类型Exchange可以让队列在绑定BindingKey 的时候…

如何优雅地单元测试 Kotlin/Java 中的 private 方法?

翻译自 https://medium.com/mindorks/how-to-unit-test-private-methods-in-java-and-kotlin-d3cae49dccd ❓如何单元测试 Kotlin/Java 中的 private 方法❓ 首先&#xff0c;开发者应该测试代码里的 private 私有方法吗&#xff1f; 直接信任这些私有方法&#xff0c;测试到…

系列四、全局配置mybatis-config.xml

一、全局配置文件中的属性 mybatis全局配置中的文件非常多&#xff0c;主要有如下几个&#xff1a; properties&#xff08;属性&#xff09;settings&#xff08;全局配置参数&#xff09;typeAliases&#xff08;类型别名&#xff09;typeHandlers&#xff08;类型处理器&am…

用前端框架Bootstrap和Django实现用户注册页面

01-新建一个名为“mall_backend”的Project 命令如下&#xff1a; CD E:\Python_project\P_001\myshop-test E: django-admin startproject mall_backend02-新建应用并注册应用 执行下面条命令依次创建需要的应用&#xff1a; CD E:\Python_project\P_001\myshop-test\mall…

模型应用系实习生-模型训练笔记(更新至线性回归、Ridge回归、Lasso回归、Elastic Net回归、决策树回归、梯度提升树回归和随机森林回归)

sklearn机械学习模型步骤以及模型 一、训练准备&#xff08;x_train, x_test, y_train, y_test&#xff09;1.1 导包1.2 数据要求1.21 导入数据1.22 数据类型查看检测以及转换1.22 划分数据 二、回归2.1 线性回归2.2 随机森林回归2.3 GradientBoostingRegressor梯度提升树回归2…

如何创建一个react项目

文章目录 前言前言打开小黑窗口npm init vite后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react.js &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&am…

84.在排序数组中查找元素的第一个和最后一个位置(力扣)

目录 问题描述 代码解决以及思想 知识点 问题描述 代码解决以及思想 class Solution { public:vector<int> searchRange(vector<int>& nums, int target) {int left 0; // 定义左边界int right nums.size() - 1; // 定义右…

uniapp原生插件之安卓获取设备唯一标识

插件介绍 安卓获取设备唯一标识&#xff0c;集成了获取imei&#xff0c;获取安卓ID&#xff0c;获取GUID&#xff0c;获取获取OAID/AAID等 插件地址 安卓获取设备唯一标识 - DCloud 插件市场 超级福利 uniapp 插件购买超级福利 详细使用文档 uniapp 安卓获取设备唯一标…