C++(26)——对象被优化以后才是最高效的C++编程

对象应用优化

我们都知道,C语言和C++在程序执行中,都是通过调用一系列的函数来实现的。并且,很多时候,编译器会帮助我们做一系列的事情,比如(在编译类的成员方法的时候,编译器默认添加 this 指针,以此来确定是哪一个对象调用了该成员方法)。得益于编译器或者说系统帮助我们做的一系列事情,我们可以更加方便地使用C++。但是凡事有利必有弊,因为系统有时候会自己调用一系列的函数,从另一个角度来说,也一定程度上降低了效率。
而我们想要提高C++的执行效率,就需要了解程序(主要是对象)使用过程中都调用了哪些方法。

先来看下面的代码示例:

#include <iostream>
using namespace std;

class Test
{
public:
	Test(int a = 10)
		:ma(a)
	{
		cout << "Test(int)" << endl;
	}
	~Test()
	{
		cout << "~Test()" << endl;
	}
	Test(const Test& t)
		:ma(t.ma)
	{
		cout << "Test(const Test&)" << endl;
	}
	Test& operator=(const Test& t)
	{
		cout << "operator=" << endl;
		ma = t.ma;
		return *this;
	}
	int getdata()const { return ma; }
private:
	int ma;
};

int main()
{
	Test t1;
	Test t2(t1);
	Test t3 = t1;
	Test t4 = Test(20); //显式生成临时对象,但是与Test t1(20)没有区别
	t4 = t1;
	return 0;
}

调用过程如下:
在这里插入图片描述

上述对象t4,编译器做了如下的优化:
c++编译器对于对象的构造的优化:用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就可以了

Test t1 = Test(20);
t1 = Test(30);//显式生成临时对象:该语句会调用t1.operator=(const Test& t),因为t1已经存在,要完成赋值操作,还必须构建一个临时对象

在这里插入图片描述

再看下面的语句:注意注释的解释

t1 = (Test)30;
//隐式转换,编译器会寻找会找构造函数中是否有带整型的构造函数int->Test(int)
t1 = 30;

继续:

Test *p = &Test(20);//p指向的是一个已经析构的临时对象了,所以不安全
const Test &ref = Test(20);//引用相当于给产生的临时对象赋予了别名,ok

上述过程中产生的临时对象,在指针指向的临时对象语句结束后立刻被析构,而引用的却不会被析构,因为相当于给临时对象赋予了别名。
结论:用指针指向临时对象是不安全的,用引用指向临时对象是安全的。

再来看下面的一个示例:注意Test类添加了一个成员变量mb,复习一下之前的几个知识点:
请添加图片描述

继续来看:以最初的Test类为例:

#include <iostream>
using namespace std;

class Test
{
public:
	Test(int a = 10)
		:ma(a)
	{
		cout << "Test(int)" << endl;
	}
	~Test()
	{
		cout << "~Test()" << endl;
	}
	Test(const Test& t)
		:ma(t.ma)
	{
		cout << "Test(const Test&)" << endl;
	}
	Test& operator=(const Test& t)
	{
		cout << "operator=" << endl;
		ma = t.ma;
		return *this;
	}
	int getdata()const { return ma; }
private:
	int ma;
};

Test GetObject(Test t) 
{
	int val = t.getdata();
	Test tmp(val);
	return tmp;
}

int main()
{
	Test t1;
	Test t2;
	t2 = GetObject(t1);

	return 0;
}

短短的几行代码,却调用了这么多次函数:
在这里插入图片描述

其实这也就是问题所在了:其具体逻辑如下:
请添加图片描述
原来的:Test GetObject(Test t)
优化后:Test GetObject(Test& t)
做这一步的目的是,减少参数传递过程中的拷贝构造,并且,也少了形参对象的析构,简而言之,少了两条调用语句:
在这里插入图片描述
继续优化:

原来的:Test tmp(val);return tmp;
优化后:return Test(val);
这一步的目的是,减少局部变量tmp的构造,以及tmp的析构。

于是,直接用临时对象取代原来定义好的局部对象,之后用临时对象来构造一个主函数上的新对象;这个时候编译器就会优化,不生成临时对象;直接构造新对象。
所以,在return的地方,会直接构造主函数栈帧上的对象:
在这里插入图片描述
其实还可以优化:
原来的:Test t2;t2 = GetObject(t1);
优化后:Test t2 = GetObject(t1);
用主函数栈帧上的临时对象拷贝新对象t2 ,于是连这个临时对象都不产生了,直接构造t2
这一步的目的是,省去主函数栈帧上的临时对象的构造和析构,并且省去了赋值函数。
也就是说,这一步优化,省去了3步!
在这里插入图片描述

于是我们总结出对象优化的原则:

  1. 不能返回局部的或者临时对象的指针或引用
  2. 函数参数传递过程中,对象优先按引用传递,不要按值传递
  3. 函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象
  4. 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值