C++面向对象总结(四)写类和对象可能影响效率的几个点

总结上一篇文章的三个点:
1.实参到形参传递的初始化的过程,我们使用引用;
(const:防止实参被修改;形参可以引用常量,常引用)
2.返回的时候,返回一个正在定义的对象,而不是一个已经定义好的对象;
3.函数调用结束接收一个对象时,用正在定义的对象接收,而不是已经存在的对象。

原理:
用初始化的方式去定义一个对象,而不是改变一个已经存在的对象;
返回一个正在定义的对象,而不是已经存在的对象;
函数参数使用引用,而不是用值接收。

实现一个test类演示这三个过程:

class test{

public:
	test(){ cout << "test()" << endl; }
	~test(){ cout << "~test()" << endl; }
	test(const test &src){ cout << "test(const test &src)" << endl;}
	void operator=(const test &src){cout << "operator=(const test &src)" << endl;}
};
//这里定义个全局函数,用来演示上述三点的效率:
test fun(test src)
{
	cout << "fun()" << endl;
	return;
}

一、实参到形参传递的过程,使用引用接收和使用对象接收的区别:

//1.使用对象接收
void fun(test src)
{
	cout << "fun()" << endl;
}
int main()
{
	test a;
	fun(a);
	return 0;
}
执行结果:
test()
test(const test &src)
fun()
~test()
~test()
请按任意键继续. . .

//2.使用引用接收实参值
void fun(test &src)
{
	cout << "fun()" << endl;
}
int main()
{
	test a;
	fun(a);
	return 0;
}
执行结果:
test()
fun()
~test()
请按任意键继续. . .

分析:少了拷贝构造函数和临时对象的析构函数;

二、返回一个对象时,返回正在定义的对象而不是已经存在的对象:

//1.返回一个已经存在的对象

test fun(test &src)
{
	cout << "fun()" << endl;
	test b = src;
	return b;
}
int main()
{
	test a;
	a = fun(a);
	return 0;
}
执行结果:
test()
fun()
test(const test &src)
test(const test &src)    //用返回对象b 拷贝构造主调函数上的临时内存
~test()
operator=(const test &src)  //主调函数上临时内存上的对象赋值给已经存在的对象a
~test()
~test()
请按任意键继续. . .

 //由于是自定义对象,所以执行函数前,就给主调函数的栈帧上开辟好了,所以返回值到主调函数的过程实际上是拷贝构造的过程;
 
// 2.返回一个正在定义的对象:
test fun(test &src)
{
	cout << "fun()" << endl;
	return test(src);
}
int main()
{
	test a;
	a = fun(a);
	return 0;
}
执行结果:

test()
fun()
test(const test &src)
operator=(const test &src)  //主调函数上临时内存上的对象赋值给已经存在的对象a
~test()
~test()
请按任意键继续. . .

分析:
由于返回值是自定义,所以返回值不会通过寄存器带回,
所以在函数调用前会在主调函数的栈帧上开辟一块内存,来接收这个返回值;

我们返回一个正在定义的对象,实际上这个临时对象不会产生
而是用生成临时对象的方式去构造主调函数内存上的这个内存,
而这个内存地址在函数压栈的时候,会告诉被调函数。

三、用一个正在定义的对象接收函数返回值,而不是已经存在的对象接收:

//1.用已经存在的对象接收返回值
test fun(test &src)
{
	cout << "fun()" << endl;
	return test();

}
int main()
{
	test a;
	test c;
	c= fun(a);
	return 0;
}
执行结果:
test()
test()
fun()
test()
operator=(const test &src)
~test()
~test()
~test()
请按任意键继续. . .



//2.用一个正在定义的对象来接收
test fun(test &src)
{
	cout << "fun()" << endl;
	return test();

}
int main()
{
	test a;
	test c = fun(a); 
	  //!!!!这里实际上fun(&c , a) 传了两个参数,一个是c的地址,一个是a对象
	return 0;
}
执行结果:
test()
fun()
test()
~test()
~test()
请按任意键继续. . .



分析:
此时主调函数上的临时内存也不会产生了, 
他会在 用 生成retrun test(src)的这个临时对象的方式去调用构造函数对象c
当然这个retrun test(src)  临时对象也不会产生。

为什么我们在函数调用时要见到函数的声明或者定义呢:
1.实参和形参的类型和个数是否匹配;
2.函数调用点返回值的类型和声明或定义时的返回值类型是否一直;
3.根据返回值类型确定返回值的带回方式。

>4字节 :一个寄存器带回
>4 <8 :两个寄存器带回
>8 :通过在主调函数产生临时量的方式带回,压栈的时候会将这块内存的地址告诉被调函数
         然后通过ebp指针偏移访问到这块临时内存。

实际上,只要是返回我们  自定义 的类型的值,都要产生临时量!!!!
不论返回值多大,都要产生临时量。

总结:
1.我们应该尽量避免临时对象的生成,更多的是用临时对象生成方式直接去构造我们的目标对象,提高效率。
2.返回一个正在定义的对象用这个临时对象,接收返回值用一个正在定义的对象接收,这样这个临时对象不会产生,主调函数上的临时内存也不会产生。

用临时对象构造同类型的正在定义的新对象时,这个临时对象不产生
将创建对象改变成“初始化”的过程,而不是“赋值“的过程;

猜你喜欢

转载自blog.csdn.net/KingOfMyHeart/article/details/89715379