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

【C++】优化函数对象:提升性能和内存效率

  • 函数对象 =》c语言里面的函数指针
  • 对象构造优化
  • 对象使用过程中背后调用的方法
  • 函数调用过程中对象背后调用方法:
  • 优化原则
  • move,forward

函数对象 =》c语言里面的函数指针

  1. 通过函数对象调用operator(),可以省略函数的调用开销,比通过函数指针调用函数(不能够inline内联调用)效率高
  2. 因为函数对象是用类生成的,所有还可以添加相关的成员变量,用来记录函数对象使用时的更多信息。
  • 使用函数对象
template<typename T>
class mygreater
{
public:bool operator()(T a,T b){return a > b;}
};
template<typename t>
class myless
{
public:
bool operator()(t a,t b)
{
return a < b;
}
};
  • 使用C语言的函数指针
template<typename T>
inline bool mygreater(T a, T b)
{
return a > b;
}
template<typename T>
inline bool myless(T a, T b)
{
return a < b;
}
template<typename T,typename Compare>
bool compare(T a,T b,Compare comp)
{
//通过函数指针调用函数,是没有办法内联的,效率低,因为有函数调用开销
return comp(a,b);//operator()(a,b)
}

把有operator()小括号运算符重载函数的对象,称作函数对象,或者称作仿函数。

对象构造优化

Test(20) 显示生成临时对象 生存周期 :所在语句
C++编译对于对象构造的优化:用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就是可以了。
Test t4(20); == Test t4 = Test(20);
//显示生成临时对象
t4 = Test(30);
t4 = (Test)30;
//隐式生成临时对象
t4 = 30;
Test *p = &Test(20);
//p指向的是一个已经析构的临时对象
const Test &ref = Test(30);

对象使用过程中背后调用的方法

Test t1(10,10); //1.Test(int,int)
int main()
{
Test t2(20,20); //3.Test(int,int)
Test t3 = t2;   //4.Test(const Test&)
static Test t4 = Test(30,30); //5.Test(int,int)
t2 = Test(20,30); //6.Test(int,int) operator= ~Test()
t2 = (Test)(30,30);//7.Test(int,int) operator= ~Test()
t2 = 6;			//8.Test(int,int) operator=~Test()
Test* p1 = new Test(30,3); //9.Test(int,int)
Test* p2 = new Test[2]; 	//10.Test(int,int) Test(int,int)
Test* p3 = &Test(60,60);  	//11.Test(int,int) ~Test()
const Test &p4 = Test(30,30); //12.Test(int,int)
delete p1;					//13~Test()
delete []p2;				//14.~Test() ~Test()
return 0;
}
Test t5(100,1000); //2.Test(int,int)//注意:5,4,1最后析构

函数调用过程中对象背后调用方法:

Test function(Test t)  	//3.Test(const Test&)
{
int val = t.getDaa();
Test tmp(val);	//4.Test(int)return tmp; 	//5.Test(const Test&)	在主函数中构造一个临时函数对象//6.~Test()//7.~Test()
}
int main()
{
Test t1; //1.Test(int)
Test t2;//2.Test(int)
t2  = function(t1);
//函数调用,实参到形参,是初始化,不是赋值//8. operator = //9.~Test() 析构5.的临时对象//10.~Test()析构2.对象//11.~Test()析构1.对象
return 0;
}

优化原则

  1. 优化一:
Test function(Test &t)  	
{
int val = t.getDaa();
Test tmp(val);	//3.Test(int)return tmp; 	//4.Test(const Test&)	在主函数中构造一个临时函数对象//5.~Test()}
int main()
{
Test t1; //1.Test(int)
Test t2;//2.Test(int)
t2  = function(t1);
//函数调用,实参到形参,是初始化,不是赋值//6. operator = //7.~Test() 析构4.的临时对象//8.~Test()析构2.对象//9.~Test()析构1.对象
return 0;
}
  1. 优化二:
Test function(Test &t)  	
{
int val = t.getDaa();			
return Test(val); 	//在主函数中直接构造一个函数对象(用临时对象构造一个新对象)//3.Test(int)不会先进行拷贝构造,	
}
int main()
{
Test t1; //1.Test(int)
Test t2;//2.Test(int)
t2  = function(t1);//4. operator = //5.~Test() 析构3.的临时对象//6.~Test()析构2.对象//7.~Test()析构1.对象
return 0;
}
  1. 优化三:
Test function(Test &t)  	
{
int val = t.getDaa();			
return Test(val); //2.Test(int) 直接构造t2		
}
int main()
{
Test t1; //1.Test(int)
Test t2 = function(t1);//又是临时对象拷贝构造同类型的新对象t2//3.~Test()析构2.对象//4.~Test()析构1.对象
return 0;
}
  1. 总结优化:
    4.1.函数参数传递过程中,对象优先按引用传递,不要按值传递
    42.函数返回对象的时候,应该优先返回一个临时对象,而不是一个定义过的对象
    4.3.接收返回值是对象的函数调用的时候,优先按初始化的方式接受,而不是按赋值的方式接收。

move,forward

  • std::move:支持移动语义
    std::move 是一个模板函数,位于 头文件中。
    它接受一个对象,并将其转换为右值引用(把左值转换成右值),使得该对象可以被移动而非复制。
    使用 std::move 可以显式地表明程序员希望将对象的所有权从一个对象转移到另一个对象,通常用于在移动语义中。
    std::move 并不会移动任何数据,它只是将一个对象标记为可移动的,告诉编译器在适当的情况下使用移动语义。
    使用 std::move 后,原对象的状态可能会被视为未定义,因此使用后需要谨慎处理原对象。
    示例:
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination = std::move(source); // 移动 source 到 destination
  • std::forward:完美转发
    std::forward 也是一个模板函数,位于 头文件中。
    它用于完美转发,即在函数模板中保持参数的原始类型(左值引用或右值引用)。
    通常在泛型编程中使用,用于将参数传递给其他函数,并保持其原始的左值或右值特性。
    std::forward 是为了解决函数参数的引用折叠规则而引入的,可以在转发时正确地保持参数的左值或右值特性。
    使用 std::forward 可以确保参数的类型在转发时得到正确保持,从而避免不必要的拷贝或移动操作。
    示例:
template<typename T>
void process(T&& arg) {another_function(std::forward<T>(arg)); // 保持参数 arg 的原始类型(左值引用或右值引用)
}
  • 综上所述,std::move 用于将对象转换为右值,支持移动语义,而 std::forward 则用于在泛型编程中保持参数的原始类型,支持完美转发。这两个函数在现代 C++ 编程中都扮演着重要的角色,用于优化性能并支持通用代码。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 第十六篇:走入计算机网络的传输层--传输层概述
  • 【Linux 运维知识】Linux 编译后的内核镜像大小
  • elementplus表单位置居中
  • SSH免秘钥问题
  • mac 安装brew并配置国内源
  • Minimax-秋招正式批-面经(SQL相关)
  • EasyExcel实现复杂Excel的导入
  • linux系统中,计算两个文件的相对路径
  • springboot中的请求过滤filter与拦截interceptor分析
  • 如何从硬盘恢复已删除/丢失的文件?硬盘恢复已删除的文件技巧
  • Windows下Python和PyCharm的应用(一)__第一个测试程序
  • linux 配置 iscsi 存储资源共享
  • 基于PI控制算法的异步感应电机转速控制系统simulink建模与仿真
  • 计算机网络 第二章: 物理层_信道复用技术
  • Linux_kernel移植uboot07
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 收藏网友的 源程序下载网
  • @jsonView过滤属性
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Angular 响应式表单 基础例子
  • Date型的使用
  • 程序员最讨厌的9句话,你可有补充?
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 回顾 Swift 多平台移植进度 #2
  • 马上搞懂 GeoJSON
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 详解NodeJs流之一
  • 详解移动APP与web APP的区别
  • 用 Swift 编写面向协议的视图
  • 主流的CSS水平和垂直居中技术大全
  • 说说我为什么看好Spring Cloud Alibaba
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​数据链路层——流量控制可靠传输机制 ​
  • # centos7下FFmpeg环境部署记录
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #define
  • #控制台大学课堂点名问题_课堂随机点名
  • (1)Hilt的基本概念和使用
  • (6)STL算法之转换
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (顺序)容器的好伴侣 --- 容器适配器
  • (算法)Game
  • (译)计算距离、方位和更多经纬度之间的点
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转)重识new
  • (转载)(官方)UE4--图像编程----着色器开发
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .Net - 类的介绍
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。