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

2024-09-07 17:52

本文主要是介绍【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++ 编程中都扮演着重要的角色,用于优化性能并支持通用代码。

这篇关于【C++】优化函数对象:提升性能和内存效率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145742

相关文章

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

Oracle的to_date()函数详解

《Oracle的to_date()函数详解》Oracle的to_date()函数用于日期格式转换,需要注意Oracle中不区分大小写的MM和mm格式代码,应使用mi代替分钟,此外,Oracle还支持毫... 目录oracle的to_date()函数一.在使用Oracle的to_date函数来做日期转换二.日

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五