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

2023-10-29

对象应用优化

我们都知道,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. 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • MSP430F5529库函数定时器B

    需提前学习 MSP430F5529库函数学习 串口 MSP430F5529库函数定时器A 捕获实验 MSP430F5529库函数定时器A 硬件PWM MSP430F5529库函数定时器A 定时中断 目录 定时器B与定时器A的不同 定时器B中
  • 三个部分,解读印刷企业MES生产管理系统

    车间MES管理系统的工作组织 传统工厂组织中 订单与生产的分配相对简单 通常在一个工作位置或一台设备上 由一个人完成一道工序或一个订单 计算产品成本也相对容易 在现代化工厂里 生产的复杂性大大提高 一台设备可同时生产多种产品 一个工人也可以
  • 【mpvue开发总结】1、引入插件 项目异常 2、数据缓存

    1 最近接到小程序直播需求 引入直播插件的时候 项目出现异常 子组件渲染不出来 解决办法 mpvue项目中引入插件 在 nextTick 通过ref调用子组件会报undefined 后来改用setTimeout 渲染正常 原理还待考究 2
  • umiJs_React学习笔记

    UmiJs搭建react项目 1 下载安装 12 22 0 14 17 0 gt 16 0 0 支持版本 yarn create umijs umi app 第三版本 根目录 umirc ts 下配置 export default defi
  • is not allowed to connect to this MariaDB server(远程连接数据库报错)

    select user host from user root localhost conf mysql uroot p123456 h192 168 100 26 ERROR 1130 HY000 Host 192 168 100 27
  • 总线测试工具 CANOE基本使用教程

    CANOE可谓是常用的总线测试工具之一 不管是总线开发工程师还是测试工程师 甚至是驻场工程师 都对它很熟悉 今天正好闲来有空 趁此机会 来分享一波CANOE的基本使用操作 其整体的框图如下所示 详细地列举了各个窗口 1 搭建CANOE工程
  • httpClient 版本不对应。

    In the first example from http htmlunit sourceforge net gettingStarted html The following line of code final HtmlPage pa
  • laravel 关联关系之多态关联

    多态关联 文章 作者与收藏的关联关系 收藏既可以是对文章的收藏 也可以是对作者的收藏 表 artist 字段 id name 表 article 字段 id title content 表 favorites 字段 id favorite
  • 我决定豁出去了,公开我做过的人工智能实战项目核心技术,有没有你心动的?

    前言 博主一直没有公开多少人工智能项目代码 但不少粉丝和朋友都很好奇真实人工智能项目到底是怎样的 我思前想后 打算针对几块我做过的人工智能项目给大家分享下 不过由于公司这两年发展比较快 项目方向有点多 不知道小伙伴们具体感兴趣哪块呢 欢迎大
  • UE4C++中如何申明“TSubclassOf”型数组

    之前查了查居然没人回答过 不废话 直接看代码 UPROPERTY EditDefaultsOnly TArray
  • C/C++面试:引用和指针的使用场合

    问 指针和引用作用都是间接引用其他对象 你如何决定何时使用指针 何时使用引用呢 应该使用指针的场合 有指向不存在对象的可能时 在任何情况下都不能使用指向空值的引用 一个引用必须总是指向某些对象 因此 如果你使用一个变量时并让它指向一个对象
  • 块设备、字符设备、裸设备和文件系统个人总结

    1 块设备 系统中可以随机访问 不需要按顺序 访问固定大小数据片 chunks 的设备称为块设备 这些数据片就称作块 硬盘是最常见的块设备 除此以外 还有软盘驱动器 CD ROM驱动器和闪存等 这里要注意 它们都是以安装文件系统的方式使用的
  • LeetCode Roman to Integer(罗马数字转换)

    思路 罗马数字是 阿拉伯数字传入之前使用的一种数码 罗马数字采用七个罗马字母作数字 即 1 X 10 C 100 M 1000 V 5 L 50 D 500 记数的方法 相同的数字连写 所表示的数等于这些数字相加得到的数 如 3 小的数字在
  • STC89C52系列单片机内部资源——中断系统

    中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的 中断功能的存在 很大程度上提高了单片机处理外部或者内部事件的能力 它也是单片机最重要的功能之一 是我们学习单片机必须要掌握 首先介绍单片机中断技术的优点 解决了快速主机与慢速I
  • FastGPT 接入飞书(不用写一行代码)

    FastGPT V4 版本已经发布 可以通过 Flow 可视化进行工作流编排 从而实现复杂的问答场景 例如联网谷歌搜索 操作数据库等等 功能非常强大 还没用过的同学赶紧去试试吧 飞书相比同类产品算是体验非常好的办公工具了 我司也是废了很大的
  • 操作系统学习(三)基本分段存储管理方式

    一 分段的定义 进程的地址空间按照自身的逻辑关系划分为若干段 例如 主程序 两个子程序 栈和一段数据 把进程分成5段 每段从0进行编址 段间要求连续 段内不要求 二 段表 1 分段系统的逻辑地址结构由段号 段名 和段内地址 段内偏移量 组成
  • uni-app中自定义动态底部tabbar(附示例源码)

    UNIAPP 自带的原生导航尽管流畅度非常好 但是在具体项目中有的时候需要动态设置以及特殊样式的 底部菜单 这个时候就需要自己去写一个自定义的底部tabbar 项目地址 fr uni app 1 比如需要特殊的图标 多出来一部分的 2 根据
  • 每日30条知识点-软件设计师知识点笔记

    立即寻址最快 寄存器寻址次之 直接寻址最慢 RISC 精简指令集计算机 特点 指令数量少 寻址方式少 长度固定 格式种类少 只提供load store指令访问存储器 以硬布线逻辑控制为主 单周期指令执行 系统总线用于主存以及外设部件连接 R
  • 数据结构和算法--树

    数据结构和算法是一种思想 理解了思想就是忘记了代码也能找回原来的记忆 二叉搜索树 二叉树 每个结点只存储一个关键字 等于则命中 小于走左结点 大于走右结点 AVL树 每个节点的左子树和右子树的高度最多差1的二叉搜索树 B B 树 多路搜索树
  • C++(26)——对象被优化以后才是最高效的C++编程

    对象应用优化 我们都知道 C语言和C 在程序执行中 都是通过调用一系列的函数来实现的 并且 很多时候 编译器会帮助我们做一系列的事情 比如 在编译类的成员方法的时候 编译器默认添加 this 指针 以此来确定是哪一个对象调用了该成员方法 得