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

2024-09-09 09:10:20 浏览数 (2)

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

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

  1. 通过函数对象调用operator(),可以省略函数的调用开销,比通过函数指针调用函数(不能够inline内联调用)效率高
  2. 因为函数对象是用类生成的,所有还可以添加相关的成员变量,用来记录函数对象使用时的更多信息。
  • 使用函数对象
代码语言:javascript复制
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语言的函数指针
代码语言:javascript复制
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;
}
代码语言:javascript复制
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);

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

代码语言:javascript复制
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最后析构

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

代码语言:javascript复制
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. 优化一:
代码语言:javascript复制
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. 优化二:
代码语言:javascript复制
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. 优化三:
代码语言:javascript复制
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 后,原对象的状态可能会被视为未定义,因此使用后需要谨慎处理原对象。 示例:
代码语言:javascript复制
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 可以确保参数的类型在转发时得到正确保持,从而避免不必要的拷贝或移动操作。 示例:
代码语言:javascript复制
template<typename T>
void process(T&& arg) {
    another_function(std::forward<T>(arg)); // 保持参数 arg 的原始类型(左值引用或右值引用)
}
  • 综上所述,std::move 用于将对象转换为右值,支持移动语义,而 std::forward 则用于在泛型编程中保持参数的原始类型,支持完美转发。这两个函数在现代 C 编程中都扮演着重要的角色,用于优化性能并支持通用代码。

0 人点赞