C++11标准中按值传递类对象参数的使用时机

2023-10-26

严正声明:本文系作者davidhopper原创,未经许可,不得转载。

作为一名资深C++程序员,在C++98、03标准时代,一直将“不得按值传递类对象参数”的规定奉为圭臬。例如:

void SetParam(const std::string& name);   // 传入类对象的常引用作为输入参数

如果谁胆敢这样写:

void SetParam(std::string name);   // 传入类对象的值作为输入参数

则一定会被认为是个C++没有入门的菜鸟。
然而到了C++11、14、17标准时代,情况发生了变化,在特定条件下居然可以按值传递类对象参数了,具体解释见Scott Meyers所著《Effective Modern C++》一书中的Item 41: Consider pass by value for copyable parameters that are cheap to move and always copied。是否有一种颠覆三观的惊讶?

一、按值传递类对象参数的示例分析

别着急,让我们来看Apollo项目中的一个具体应用:

// 在modules/planning/common/reference_line_info.h文件中
void SetCandidatePathData(std::vector<PathData> candidate_path_data);

该函数的实现如下:

// 在modules/planning/common/reference_line_info.cpp文件中
void ReferenceLineInfo::SetCandidatePathData(
    std::vector<PathData> candidate_path_data) {
  // 传入的类对象在函数体内有被复制的需求(通过移动语义)
  candidate_path_data_ = std::move(candidate_path_data);
}

该函数首先使用std::move函数将candidate_path_data从左值(lvalue)转换为右值(rvalue),然后调用移动赋值运算符(move assignment operator),将candidate_path_data的内容移动到candidate_path_data_

左值与右值的解释
凡是能真正存储于内存而不是寄存器中的值就是左值,其余的都是右值。更简单的说法是:凡是可以进行取地址(&)操作的值都是左值,其余都是右值。

// lvalues:
int i = 42;
i = 43; // ok, i is a lvalue 
int* p = &i; // ok, i is a lvalue 
int& foo();
foo() = 42; // ok, foo() is a lvalue
int* p1 = &foo(); // ok, foo() is a lvalue
void SetPtr(std::unique_ptr<std::string>&& ptr) {
  // ptr is a lvalue. Because it can be taken the address.
  // std::move(ptr) is a rvalue. Because it can't be taken the address.
  auto p = std::move(ptr); 
}

// rvalues: 
int foobar(); 
int j = 0;
j = foobar(); // ok, foobar() is a rvalue
int k = j + 2; // ok, j+2 is a rvalue
int* p2 = &foobar(); // error, cannot take the address of a rvalue 
j = 42; // ok, 42 is a rvalue

std::move函数的解释
std::move函数的定义如下:

// in namespace std
template<typename T>
typename remove_reference<T>::type&& move(T&& param) {
  using ReturnType = typename remove_reference<T>::type&&;
  return static_cast<ReturnType>(param);
}

typename remove_reference<T>::type的意思是将类型T的引用给去除,即将T&T&&全部变为T,但不去除constvolatile属性(可通过typename remove_cv<T>::type去除)。例如std::remove_reference<int>::typestd::remove_reference<int &>::typestd::remove_reference<int &&>::type的类型均为int,但std::remove_reference<const int>::typestd::remove_reference<const int &>::typestd::remove_reference<const int &&>::type的类型均为const int
可见,std::move函数并不进行任何移动操作,它只是无条件地将传入的参数转换为右值,不需要消耗任何计算资源,其实换个形如std::rvalue_cast的名称更为恰当。

Apollo项目目前对于该函数的使用有两处,分别是:

第一种调用

// modules/planning/tasks/optimizers/piecewise_jerk_path/
// piecewise_jerk_path_optimizer.cc文件第128行
reference_line_info_->SetCandidatePathData(std::move(candidate_path_data));

第二种调用

// modules/planning/tasks/deciders/path_assessment_decider/
// path_assessment_decider.cc文件第231行
// 此处调用语法上正确,效率上错误
reference_line_info->SetCandidatePathData(new_candidate_path_data);

对于第一种调用reference_line_info_->SetCandidatePathData(std::move(candidate_path_data));,因为std::move(candidate_path_data)candidate_path_data转换为右值,于是SetCandidatePathData(std::vector<PathData> candidate_path_data)中的std::vector<PathData> candidate_path_data调用移动构造函数(move constructor)传递参数,整个调用使用了一次移动构造函数及一次移动赋值运算符

对于第二种调用reference_line_info->SetCandidatePathData(new_candidate_path_data);,因为未将参数转换为右值,于是SetCandidatePathData(std::vector<PathData> candidate_path_data)中的std::vector<PathData> candidate_path_data调用拷贝构造函数(copy constructor)传递参数,整个调用使用了一次拷贝构造函数及一次移动赋值运算符。第二种资源消耗高,从效率方面而言是不正确的调用

第三种调用

如果使用C++98标准倡导的引用传递参数方式:

void ReferenceLineInfo::SetCandidatePathData(
    const std::vector<PathData>& candidate_path_data) {
  candidate_path_data_ = candidate_path_data;
}
// 对于上述引用传递方式,下面两种调用方式等价。
reference_line_info->SetCandidatePathData(new_candidate_path_data);
reference_line_info->SetCandidatePathData(std::move(new_candidate_path_data));

那么整个调用会使用一次拷贝赋值运算符(copy assignment operator)。

一般而言,拷贝赋值运算符拷贝构造函数的代价可视为相同,移动赋值运算符移动构造函数的代价可视为相同,并且拷贝代价远大于移动低价。于是,第一种调用耗费两次移动操作,第二种调用耗费一次拷贝操作和一次移动操作,第三种调用耗费一次拷贝操作,因此整体效率比较:

第一种调用>第三种调用>第二种调用

二、替代方案

仍以Apollo项目中的代码为例:

// 在modules/planning/common/reference_line_info.h文件中
void SetCandidatePathData(std::vector<PathData> candidate_path_data);

// 在modules/planning/common/reference_line_info.cpp文件中
void ReferenceLineInfo::SetCandidatePathData(
    std::vector<PathData> candidate_path_data) {
  // 传入的类对象在函数体内有被复制的需求(通过移动语义)
  candidate_path_data_ = std::move(candidate_path_data);
}

进行说明,下面给出两种替代方案。

2.1 重载引用方案

使用接收左值常引用、右值引用参数的两个重载函数,代码如下:

// 在modules/planning/common/reference_line_info.h文件中
// 重载函数1:左值常引用版本
void SetCandidatePathData(const std::vector<PathData>& candidate_path_data);
// 重载函数2:右值引用版本
void SetCandidatePathData(std::vector<PathData>&& candidate_path_data);

// 在modules/planning/common/reference_line_info.cpp文件中
// 重载函数1:左值常引用版本,需要一次拷贝赋值运算符
void ReferenceLineInfo::SetCandidatePathData(
    const std::vector<PathData>& candidate_path_data) {  
  // 使用拷贝赋值运算符
  candidate_path_data_ = candidate_path_data;
}

// 重载函数2:右值引用版本,仅需要一次移动赋值运算符
void ReferenceLineInfo::SetCandidatePathData(
    std::vector<PathData>&& candidate_path_data) {  
  // 使用移动赋值运算符
  candidate_path_data_ = std::move(candidate_path_data);
}
  • 优点:当传入右值引用参数时,比第一节中的按值传参版本少调用一次移动构造函数(及对应的析构函数);
  • 缺点:需要维护两个重载函数。如果传入两个、三个需要拷贝的参数,那么需要维护多少个重载函数???

2.2 通用引用方案

通用引用(universal reference)是Scott Meyers所著《Effective Modern C++》一书中提出的概念。对于包含两个地址符(&&)的参数,一般都是右值引用,但两种情形除外,属于通用引用。最常见的情形是函数模板参数(function template parameters),示例代码如下所示:

// param is a universal reference
template<typename T> void f(T&& param);

第二种情形是自动类型推断(auto type declarations),示例代码如下所示:

// var2 is a universal reference
auto&& var2 = var1;

上述两种情形的共同特点是均存在编译器类型推断。只要不存在类型推断的情形,均不属于通用引用,而是右值引用,示例代码如下:

// no type deduction; param is an rvalue reference
void f(Widget&& param); 
// no type deduction; var1 is an rvalue reference
Widget&& var1 = Widget(); 

2.2.1 通用引用方案一

对于第一节中的示例,给出第一种通用引用的实现方案:

// 在modules/planning/common/reference_line_info.h文件中
// 注意函数模板只能位于头文件中

// 1. ReferenceLineInfo类内部函数声明
// 外部接口(public属性)
template<typename T>
void SetCandidatePathData(T&& candidate_path_data);
// 内部实现函数(private属性)
template <typename T>
void SetCandidatePathDataImpl(T&& candidate_path_data, std::true_type);

// 2.ReferenceLineInfo类外部作为内联函数实现,虽然最终可能无法内联,但函数模板只能
// 放在modules/planning/common/reference_line_info.h文件中实现。
template<typename T>
inline void ReferenceLineInfo::SetCandidatePathData(
    T&& candidate_path_data) {  
    // 借助辅助函数实现
    SetCandidatePathDataImpl(
      std::forward<T>(candidate_path_data),
      std::is_same<std::vector<PathData>, typename std::decay<T>::type>());
}
// 内部实现函数
template <typename T>
inline void ReferenceLineInfo::SetCandidatePathDataImpl(T&& candidate_path_data,
                                         std::true_type) {  
  // 使用完美转发引用
  candidate_path_data_ = std::forward<T>(candidate_path_data);
}

上述实现中,std::forward<T>(candidate_path_data)表示完美转发,如果传入的实参(argument)candidate_path_data是一个左值,则std::forward<T>(candidate_path_data)是一个左值;如果candidate_path_data是一个右值,则std::forward<T>(candidate_path_data)是一个右值。typename std::decay<T>::type表示将T去除左值引用(&)、右值引用(&&)和constvolatile属性后的类型,例如:typename std::decay<const int&&>::type的类型为int。如果typename std::decay<T>::typestd::vector<PathData>类型相同,则std::is_same<std::vector<PathData>, typename std::decay<T>::type>()是继承自std::integral_constant<bool, true>(别名为std::true_type)的结构体对象,否则是继承自std::integral_constant<bool, false>(别名为std::false_type)的结构体对象。std::true_type是结构体std::integral_constant<bool, true>的别名(typedef)。

  • 优点:当传入右值引用参数时,比第一节中的按值传参版本少调用一次移动构造函数(及对应的析构函数);
  • 缺点:需要维护一个语法奇怪的完美转发实现函数。

2.2.2 通用引用方案二

对于第一节中的示例,给出第二种通用引用的实现方案:

// 在modules/planning/common/reference_line_info.h文件中
// 注意函数模板只能位于头文件中

// 1. ReferenceLineInfo类内部函数声明
template <
    typename T,
    typename Cond = typename std::enable_if<std::is_same<
        std::vector<PathData>, typename std::decay<T>::type>::value>::type>
void SetCandidatePathDataCond(T&& candidate_path_data);


// 2.ReferenceLineInfo类外部作为内联函数实现,虽然最终可能无法内联,但函数模板只能
// 放在modules/planning/common/reference_line_info.h文件中实现。
template <typename T, typename Cond>
inline void ReferenceLineInfo::SetCandidatePathDataCond(T&& candidate_path_data) {
  // 使用完美转发引用
  candidate_path_data_ = std::forward<T>(candidate_path_data);
}

上述实现中,typename Cond = typename std::enable_if<std::is_same<std::vector<PathData>, typename std::decay<T>::type>::value>::type>是一个默认值为typename std::enable_if<std::is_same<std::vector<PathData>, typename std::decay<T>::type>::value>::type>的条件模板参数。typename std::enable_if<std::is_same<std::vector<PathData>, typename std::decay<T>::type>::value>::type>表示如果typename std::decay<T>::typestd::vector<PathData>类型相同,则函数void SetCandidatePathDataCond(T&& candidate_path_data)可以编译通过,否则会编译失败,即该函数只接受类型为std::vector<PathData>std::vector<PathData>&std::vector<PathData>&&const std::vector<PathData>const std::vector<PathData>&const std::vector<PathData>&&volatile std::vector<PathData>volatile std::vector<PathData>&volatile std::vector<PathData>&&的实参。

  • 优点:当传入右值引用参数时,比第一节中的按值传参版本少调用一次移动构造函数(及对应的析构函数),且只需维护一个实现函数;
  • 缺点:需要维护一个语法更奇怪的完美转发实现函数。

三、结论

何时使用按值传递类对象参数?

  • 传入的类对象必须要在函数体内被复制(通过移动语义或拷贝方式),说得更直白一点,就是要接收外部传入的类对象参数(构造函数或Set函数)。如果没有该需求,一律使用C++98标准倡导的引用传递参数方式
  • 该类的移动操作必须比拷贝操作的效率要高得多,如果不满足该需求,一律使用C++98标准倡导的引用传递参数方式
  • 该类必须已经实现了移动构造函数和移动赋值运算符;
  • 必须使用std::move()函数将被传入的参数转换为右值,以便使用移动语义(即调用移动构造函数或移动赋值运算符);
  • 不要将基类对象通过传值方式传递;
  • 对于只可移动而不可拷贝的类对象,例如:std::unique_ptr<std::string>std::futurestd::thread,只能使用按右值引用方式传递参数(整个调用只需耗费一次移动操作),示例代码如下:
class Widget {
public:
  ...
  void SetPtr(std::unique_ptr<std::string>&& ptr) {
    p = std::move(ptr); 
  }
private:
  std::unique_ptr<std::string> p;
};

调用代码必须类似如下操作:

Widget w;
...
w.SetPtr(std::make_unique<std::string>("Modern C++"));
// or
auto title = std::make_unique<std::string>("Modern C++");
w.SetPtr(std::move(title));
  • 在按值传递类对象参数的前提下,千万不要在类对象前加const修饰符,否则将变为彻头彻底的低效率拷贝传递,示例代码如下:
// 注意在类对象前加const修饰符是完全错误的用法,会变为彻头彻底的低效率拷贝传递
// 下面是错误代码,千万不要效仿!!!
void ReferenceLineInfo::SetCandidatePathData(
    const std::vector<PathData> candidate_path_data) {
  candidate_path_data_ = std::move(candidate_path_data);
}

上述做法完全错误,因为const对象不能被移动,会导致传递参数时无法使用移动赋值运算符,转而使用低效的拷贝赋值运算符!

最后,请记住Scott Meyers的忠告,谨慎使用按值传递类对象参数
C++11 doesn’t fundamentally change the C++98 wisdom regarding pass by value. In general, pass by value still entails a performance hit you’d prefer to avoid, and pass by value can still lead to the slicing problem. What’s new in C++11 is the distinction between lvalue and rvalue arguments. Implementing functions that take advantage of move semantics for rvalues of copyable types requires either overloading or using universal references, both of which have drawbacks. For the special case of copyable, cheap-to-move types passed to functions that always copy them and where slicing is not a concern, pass by value can offer an easy-to-implement alternative that’s nearly as efficient as its pass-by-reference competitors, but avoids their disadvantages.

C ++ 11并没有从根本上改变C ++ 98关于传值方式的考量。 通常,应尽可能避免按值传递以免造成性能损失,并且按值传递还可能导致基类与派生类对象之间的所谓截断问题(slicing problem)。和C++ 98相比, C ++ 11的新情况是区分左值和右值。 利用可拷贝类型的右值实现移动语义,通常需要重载两个函数或使用通用引用,二者均有缺陷。 对于可拷贝且移动代价低的类对象,如不涉及基类与派生类之间的截断问题,按值传递参数通常可以做到与引用传递参数方式的效率相当,但无需考虑代码膨胀的问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11标准中按值传递类对象参数的使用时机 的相关文章

  • Towards 3D Human Pose Estimation in the Wild: a Weakly-supervised Approach论文翻译

    论文地址 https arxiv org abs 1704 02447 code https github com xingyizhou pytorch pose hg 3d Towards 3D Human Pose Estimation
  • 【机器学习】浅谈 模型大小与推理速度

    目录 一 前言 二 常用模型大小评估指标 2 1 计算量 2 2 参数量 2 3 访存量 2 4 内存占用 2 5 小结 三 计算量越小 模型推理就越快吗 3 1 计算密度与 RoofLine 模型 3 2 计算密集型算子与访存密集型算子
  • 逍遥模拟器导出文件到电脑

    1 确保已经成功连接模拟器 如有问题 请查看 https blog csdn net qq 15228737 article details 81142905 2 用cd命令找到要导出的文件夹 文件 再将该文件夹 文件导出到电脑上 如下图
  • React学习--JSX与react事件

    1 JSX 1 1 表达式 在React中使用JSX来描述HTML页面 而且可以与js混合使用 使用JavaScript表达式时要将表达式包含在大括号里 const user firstName Harper lastName Perez
  • 关于虚方法virtual和抽象方法以及多态的研究

    虚方法virtual定义 当类中的方法声明前加上了virtual 修饰符 我们称之为虚方法 反之为非虚 使用了virtual 修饰符后 不允许再有static abstract 或override 修饰符 必须是Public 为了实现多态
  • 御剑WEB指纹识别系统教程,图文教程(超详细)

    作者简介 CSDN top100 阿里云博客专家 华为云享专家 网络安全领域优质创作者 推荐专栏 对网络安全感兴趣的小伙伴可以关注专栏 网络安全入门到精通 御剑WEB指纹识别 一 基本使用 二 内置字典 三 自定义字典 四 扫描原理 御剑W
  • Content-Type 属性值

    数据请求headers常见的Content Type值 Content Type 属性值众多 每个值都有其特定的作用和使用场景 下面是一些常见的 Content Type 值和对应的作用 Content Type application x
  • 算法-宽度优先搜索

    算法 宽度优先搜索 一 宽度优先搜索 广度优先或横向优先搜索 是一种图形搜索算法 简单的说 BFS是从根节点开始 沿着树的宽度遍历树的节点 如果所有节点均被访问 则算法中止 DFS Depth First Search 深度优先搜索 对每一
  • 解决Mac版微信小程序开发工具打开后无法显示二维码

    问题描述 正常情况下 打开微信小程序开发工具后 首页提示扫描二维码进行登陆 但是如果不显示二维码 当然无法登陆 解决方案 无法显示二维码肯定是程序运行哪里出错了 我们直接点击桌面图标是无法排查错误 所以需要切换至命令行进行启动 打开命令行
  • 华为交换机开局配置

    sysname SWITCH aaa undo local aaa user password policy administrator local user admin password irreversible cipher admin
  • 四、无人机知识笔记(初级:基本运动原理)

    笔记来源于 沈阳无距科技 工业级无人机的中国名片 编程外星人 目录 一 多旋翼直升机 二 基本飞行姿态 三 多旋翼飞行原理 四 反扭力与偏航运动 五 螺旋桨 六 有刷电机和无刷电机 七 电调与PWM信号 八 动力电池 九 遥控器 十 机架设
  • C++ ——STL(上)

    前言 STL基本概念 1 STL Standard Template Library 标准模板库 2 STL从广义上分为 容器 container 算法 algorithm 迭代器 iterator 3 容器和算法之间通过迭代器进行无缝连接
  • SQL注入***

    SQL注入 是 对数据库进行 的常用手段之一 随着B S模式应用开发的发展 使用这种模式编写应用程序的程序员也越来越多 但是由于程序员的水平及经验也参差不齐 相当大一部分程序员在编写代码的时候 没有对用户输入数据的合法性进行判断 使应用程序
  • npm 安装卸载模块 & ionic插件安装与卸载

    npm安装模块 npm install xxx利用 npm 安装xxx模块到当前命令行所在目录 npm install g xxx利用npm安装全局模块xxx 本地安装时将模块写入package json中 npm install xxx
  • 用回车键实现MFC对话框中TAB键控制输入焦点在控件中跳转的效果

    用回车键实现MFC对话框中TAB键控制输入焦点在控件中跳转的效果 以下转载自 http blog csdn net ghgk archive 2004 10 20 144795 aspx 近日在为一个数据应用写数据输入界面 大量的编辑框要想
  • maven本地仓库批量上传私服

    最近做项目遇到一个问题 从别的地方拿过来的工程是用maven构建的 而且用了许多自有的依赖包 如果一个个的上传到私服简直是浪费时间 于是用Python 写了一个批量上传的工具 简化自己的工作量 使用的是Python3 分别依赖了os sub
  • Run-Time Check Failure #2 - Stack around the variable ‘password(数组名)‘ was corrupted.

    记录一些小小的bug 使用visaul 2019编辑c语言程序 程序可以正常运行 但程序结束退出时忽然报错闪退 原来是我定义password 数组时没有规定数组大小 导致栈溢出 正确的做法只要给数组规定一个大小就行了 以下讲解来自chatg
  • 芯片的英文手册需要全部看吗?

    以下文章来源于 公 众 号开源电子网 读取更多技术文章 请扫码关注 芯片的英文手册需要全部看吗 进入嵌入式行业的人都知道 必定和英文手册打交道的 例如使用一款PHY芯片设置电路 那么首要任务就是查找该PHY芯片的手册以及相关资料 这些资料呢
  • 用房屋建筑方式理解软件构建

    如果你是在建一栋房子 那么这个建造过程就会复杂得多 而糟糕的设计所引发的后果也更严重 首先你要决定准备建一个什么类型的房子 在软件开发里的类似事项称为问题定义 problem definition 接下来 你必须和某个建筑师 archite
  • java代码规范(转载整合)

    1 标识符命名规范 1 1 概述 标识符的命名力求做到统一 达意和简洁 1 1 1 统一 统一是指 对于同一个概念 在程序中用同一种表示方法 比如对于供应商 既可以用supplier 也可以用provider 但是我们只能选定一个使用 至少

随机推荐

  • 数据库文件被.eight后缀勒索病毒加密?不要惊慌,91数据恢复专家教您应对处理!

    当企业遭受勒索病毒攻击时 数据库文件被加密 这会导致企业无法访问其重要的业务数据 会给企业带来较大的困扰 本篇文章 91数据恢复专家将会针对 tsai shen zohomail eu eight勒索病毒 介绍如何恢复被 tsai shen
  • keras部分介绍

    1 keras 关于batch normalization函数参数axis 3的解释 https www zhihu com question 318398221 tips BN层的作用 1 加速收敛 2 控制过拟合 可以少用或不用Drop
  • 2023年面试问答-二分查找

    正常实现 Input 1 2 3 4 5 key 3 return the index 2 public int binarySearch int nums int key int l 0 h nums length 1 while l l
  • 一键端2

    新仙剑ol 链接 http pan baidu com s 1i3xZHLb 密码 hyly 链接 http pan baidu com s 1c0HgGKw 密码 druj 本帖隐藏的内容 龙之谷133服务端 链接 http pan ba
  • 索琦c语言程序设计第二版第七章,C语言程序设计自学考试大纲(2000年7月版).doc...

    天津市高等教育自学考试课程考试大纲 课程名称 应用程序基础及设计 课程代码 1115 4874 第一部分 课程性质与设置目的 一 课程性质与特点 应用程序基础及设计 课程是高等教育自学考试电子信息工程专业的必修课 是该专业一门重要的基础课
  • Qt实现TCP客户端和服务器通讯程序

    复习的心态过一遍之前基础的一些东西 Qt封装了QTcpServer和QTcpSocket两个类 其中QTcpServer继承自QObject 通过listen 函数监听传入的客户端连接 当Client连接上时 QTcpServer会发出ne
  • 使 QComboBox 下拉一个带复选框的树形列表

    背景 在项目开发过程中需要使 QComboBox 下拉一个树形列表 直接通过 setModel 和 setView 设置 combox 控件可以实现 但是在单击节点箭头按钮时也会隐藏下拉框的显示 因此需要重新实现 QComboBox 的方法
  • 解决vue 路由传参 页面刷新或者后退参数丢失的问题

    vue路由传递参数如果用params传递参数 那么页面刷新就会丢失数据 可以改用query来传递参数 这样刷新就不会丢失 this router push path business bizInspectionTaskSub query f
  • Db2 v11.1 Upgrade to V11.5 HADR 模式

    https www ibm com docs en db2 11 5 topic methods universal versus product specific fix packs Universal versus product sp
  • RocketMQ重置消费位点源码分析

    这里是weihubeats 觉得文章不错可以关注公众号小奏技术 文章首发 拒绝营销号 拒绝标题党 背景 最近在使用RocketMQ的重置消费位点的时候经常出现报错 所以就打算研究下RocketMQ是如何重置消费者的消费位点的 RocketM
  • 图像形态学处理(膨胀腐蚀开闭运算)——数字图像处理学习八(C++版)

    一 基本概念 1 形态学的基本思想是利用一种特殊的结构元来测量或提取输入图像中相应的形状或特征 以便进一步进行图像分析和目标识别 2 图像的形态学处理是对二值图像进行处理 所以在形态学处理前需要将图像二值化 3 结构元可以是任意形状 结构元
  • 可信执行环境(TEE)技术介绍(Trusted Execution Environment)

    本文对当前流行的移动终端TEE技术做简要概述 并对一些细节展开讨论 1 当前移动安全背景 当前移动终端面临这严重的安全威胁 威胁点如下图所示 因此移动厂商 用户 服务提供商等各方都对移动安全提出了强烈的需求 2 REE介绍 Rich Exe
  • 线程安全

    线程安全 多线程的执行顺序不可重现 但是必须要求执行结果必须可以重现 线程的共享数据操作不完整性就一定会出现数据被破坏 而导致结果无法预知的问题 线程的安全问题 同步处理的引入 在java语言中存在两种内建的synchronized语法 s
  • ng-model

    ng model指令用来将input select text area或自定义表单控件同包含它们的作用域中的属性进行绑定 它可以提供并处理表单验证功能 在元素上设置相关的CSS类 ng valid ng invalid等 并负责在父表单中注
  • 【解决】2021-07-30Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException:

    Caused by org springframework beans factory BeanCreationException Error creating bean with name sqlTemplate defined in U
  • python实现向图像随机添加高斯白噪声,并修改尺寸

    基于python向图像随机添加高斯噪声 并修改尺寸 噪声分布设置为 均值为0 方差分布在0 50之间 coding utf 8 Created on Nov Mon 29 14 09 45 2021 author 瀛台夜雪 import o
  • 【opencv】基于opencv实现人脸识别,从环境搭建到代码实现(超详细教程)

    前言 目标 让计算机通过训练做到认识我或者检测出视频中的人是谁 本文是一个学习笔记 记录一下自己的实现过程 在实现过程中遇到的问题以及个人对知识的理解 一 环境配置 1 软件安装 首先先安装一下必须的软件 python pycharm op
  • 通俗易懂的java设计模式(6)-代理模式

    1 什么是代理模式 为某个对象提供一个代理对象 通过这个代理对象 可以控制对原对象的访问 通俗的解释 我们电脑桌面上的一个个快接方式 当我们点击这个快捷方式的时候 我们就间接地访问到了这个程序 2 静态代理 何为静态 即在程序运行之前 代理
  • java后端解决重复提交问题

    一 为什么会出现重复提交 主要是由于网络的延迟问题以及页面刷新的操作 二 表单的重复提交会导致的问题 主要能够造成很多脏数据 三 解决的办法 3 1 前端解决办法 通过前端的方法将提交按钮变灰 对于前端的办法这里就不做演示了 因为前端的控制
  • C++11标准中按值传递类对象参数的使用时机

    严正声明 本文系作者davidhopper原创 未经许可 不得转载 作为一名资深C 程序员 在C 98 03标准时代 一直将 不得按值传递类对象参数 的规定奉为圭臬 例如 void SetParam const std string nam