C++对象调用优化

C++对象调用优化

临时对象拷贝构造新对象,临时对象就不会产生!!!

常见的对象调用过程

c++编译器对于对象构造的优化:用临时对象拷贝新对象的时候,临时对象就不产生了,直接构造新对象就可以了

#include<iostream>
using namespace std;class Test
{
public:Test(int data = 10) : ma(data), mb(data) { cout << "Test(int)" << endl; }~Test() { cout << "~Test()" << endl; }Test(const Test &t) { cout << "Test(&)" << endl; }Test &operator=(const Test &t) { cout << "operator=" << endl; }private:int ma;int mb;
};
int main()
{Test t1;//普通构造函数Test t2(t1);//拷贝构造函数Test t3 = t1; // 拷贝构造函数t2 = t3;//拷贝构造函数// 这里和Test t4(20)没有区别,并不会产生Test(20)临时对象Test t4 = Test(20);//普通构造函数cout << "-------------------" << endl;//显示调用构造函数生成临时对象t4 = Test(30); // 普通构造函数,结束这条语句就会调用析构函数t4 = (Test)30; // 普通构造函数,结束这条语句就会调用析构函数//隐式调用构造函数生成临时对象t4 = 30; // 普通构造函数,结束这条语句就会调用析构函数cout << "-----------------" << endl;return 0;
}


const Test &t1 = Test(20, 20);//仅仅调用了普通构造函数,不会产生临时对象(所以不会有析构)。

对象创建顺序

需要注意的是:static Test t4 = Test(30, 30);这里编译器会优化,临时对象生成新对象,不会产生临时对象,等价于static Test t4(30,30)。Test *p3 = &Test(80, 80); ,这里语句结束后就被析构,p3就是空指针。const Test &p4 = Test(90, 90); ,引用变量就会保存临时对象,所以不会被析构。静态对象的析构在作用域结束之后才进行析构。

#include<iostream>
using namespace std;class Test
{
public:Test(int x = 10, int y = 10) : ma(x), mb(y) { cout << "Test(int,int)" << endl; }~Test() { cout << "~Test()" << endl; }Test(const Test &t) { cout << "Test(&)" << endl; }Test &operator=(const Test &t) { cout << "operator=" << endl; }private:int ma;int mb;
};
Test t1(10, 10);//1.Test(int,int)
int main()
{Test t2(20, 20);//3.Test(int,int)Test t3 = t2;   // 4.Test(&)// 这里编译器会优化,临时对象生成新对象,不会产生临时对象,等价于static Test t4(30,30)static Test t4 = Test(30, 30); // 5.Test(int,int),t2 = Test(40, 40);             // 6.Test(int,int),operator=,~Test()t2 = (Test)(50, 50);           // 7.Test(int,int),operator=,~Test()t2 = 60;                       // 8.Test(int,int),operator=,~Test()Test *p1 = new Test(70, 70);   // 9.Test(int,int)Test *p2 = new Test[2];        // 10.Test(int,int),Test(int,int)// Test *p3 = &Test(80, 80);      //可能会报错// 11.Test(int,int),~Test(),这里语句结束后就被析构,p3就是空指针const Test &p4 = Test(90, 90); // 12.Test(int,int),引用变量就会保存临时对象,所以不会被析构delete p1;                     // 13.~Test()delete[] p2;                   // 14.~Test()return 0;
}
Test t5(100, 100);//2.Test(int,int)
/*
剩下的析构顺序,p4->t3->t2->t4->t5->t1
t4在t3和t2后面析构的原因是它是静态变量,存储在数据段中,所以在后面析构
*/

函数调用存在的问题

函数调用过程中存在很多对象优化的问题,后面会具体介绍怎么去优化。

#include<iostream>
using namespace std;class Test
{
public:Test(int data = 10) : ma(data) { cout << "Test(int)" << endl; }Test(const Test& t) { cout << "Test(&)" << endl; }Test& operator=(const Test& t) { cout << "operator=" << endl; return *this; }~Test() { cout << "~Test()" << endl; }int getData() { return ma; }private:int ma;
};
Test getObj(Test t)
{int val = t.getData();Test temp(val);return temp;
}
int main()
{Test t1;Test t2;cout << "------------------" << endl;t2 = getObj(t1);cout << "------------------" << endl;Test t3 = getObj(t2);cout << "------------------" << endl;return 0;
}


函数对象调用过程图。

函数的优化:尽量传递引用,而不是传值;尽量返回临时对象,而不是在函数内部构造出对象。优化后的代码如下所示:

Test getObj(Test& t)
{int val = t.getData();//Test temp(val);return Test(val);
}

可以减少实参到形参的拷贝构造,以及函数内部对象的构造和析构。

函数调用优化总结!!!

函数传递过程中,对象优先按照引用传递,不要按值传递

函数返回对象时,尽量返回一个临时对象,而不是返回一个已经定义好的对象

接收返回值是对象的时候,尽量以初始化的形式接收,而不是以赋值的形式接收

具体可以函数调用存在的问题部分讲解和分析!!!

String的右值引用拷贝构造和操作符重载

#include<iostream>
#include<string.h>using namespace std;class String
{
public:String(const char* p = nullptr){if (p != nullptr){_pstr = new char[strlen(p) + 1];strcpy(_pstr, p);}else{_pstr = new char[1];*_pstr = '\0';}cout << "String(const char*)" << endl;}~String(){cout << "~String()" << endl;delete[] _pstr;_pstr = nullptr;}String(const String& src){cout << "String(const String&)" << endl;_pstr = new char[strlen(src._pstr) + 1];strcpy(_pstr, src._pstr);}String(String&& src){//因为是临时对象,所以就没有用const修饰cout << "String(const String&&)" << endl;_pstr = src._pstr;src._pstr = nullptr;}String& operator=(const String& src){cout << "String& operator=(const String&)" << endl;if (this == &src)return *this;delete[] _pstr;_pstr = new char[strlen(src._pstr) + 1];strcpy(_pstr, src._pstr);return *this;}String& operator=(String&& src){//右值引用的操作符重载函数cout << "String& operator=(String&&)" << endl;if (this == &src)return *this;delete[] _pstr;_pstr = src._pstr;src._pstr = nullptr;return *this;}bool operator>(const String& src) const{return strcmp(_pstr, src._pstr) > 0;}bool operator==(const String& src) const{return strcmp(_pstr, src._pstr) == 0;}bool operator<(const String& src) const{return strcmp(_pstr, src._pstr) < 0;}int length()const{return strlen(_pstr);}char& operator[](int index) { return _pstr[index]; }const char& operator[](int index) const { return _pstr[index]; }const char* c_str() const { return _pstr; }private:char* _pstr;friend String operator+(const String& lsrc, const String& rsrc);friend ostream& operator<<(ostream& out, const String& src);friend istream& operator>>(istream& in, String& src);
};String operator+(const String& lsrc, const String& rsrc)
{char* temp = new char[strlen(lsrc._pstr) + strlen(rsrc._pstr) + 1];strcpy(temp, lsrc._pstr);strcat(temp, rsrc._pstr);String s(temp);delete[] temp;return s;
}
ostream& operator<<(ostream& out, const String& src)
{out << src._pstr;return out;
}
istream& operator>>(istream& in, String& src)
{in >> src._pstr;return in;
}int main()
{String str1;String str2 = "aaa";String str3 = "bbb";String str4 = str2 + str3;String str5 = str2 + "ccc";String str6 = "ddd" + str2;cout << "----------------------------------" << endl;String str7 = String("hello,world");//C++会优化,直接调用构造函数,不会产生临时对象str6 = String("hello") + String(",world");//先调用两个构造函数,在调用operator+重载,cout << "----------------------------------" << endl;return 0;
}

没有右值操作符和拷贝构造函数的情况:


设置了带右值引用的拷贝构造和操作符重载函数。


可以看到原来的使用临时对象生成对象中的函数全部替换为带右值引用的拷贝构造函数,赋值操作符重载也换为带右值引用的拷贝构造函数。使用右值引用的好处主要是节省拷贝构造和操作符重载过程中资源的开辟和释放

String在vector上的使用

move && forward

因为右值引用变量本身是一个左值,所以在一个函数传递右值引用变量的过程中会出现问题,可以如下所示:

void construct(T* p, const T& val)//对象创建
{// 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void construct(T* p, T&& val)//对象创建
{// 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733//这里虽然val是一个右值引用变量,但是还是会调用左值的拷贝构造函数new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(T&& val)
{if (full())expand();//虽然传入的val是一个右值引用变量,但是它本身是一个左值,//所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数_allocator.construct(_last, val);_last++;
}

move:移动语义,强制获得右值类型。可以实现将一个左值转化为一个右值(这个值其实原来可能就是右值引用变量)。
forward:类型的完美转化,能够识别左值和右值类型。如果原来是左值,那么还是左值;如果原来是右值,那么就传递为右值。

String& &&val->String&一个引用加两个引用还是一个引用。
String&& &&val->String&两个引用加两个引用还是两个引用。
String&& &val->String&两个引用加一个引用还是一个引用,因为右值引用其实本身就是就是一个左值。

后面的val其实就是函数传递过程中的形参类型。

所以上面代码可以考虑更改为以下两种类型:

方式一:move强制转化

void construct(T* p, const T& val)//对象创建
{// 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void construct(T* p, T&& val)//对象创建
{// 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733new (p) T(std::move(val));//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(const T& val)
{if (full())expand();//虽然传入的val是一个右值引用变量,但是它本身是一个左值,//所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数_allocator.construct(_last, val);_last++;
}
void push_back(T&& val)
{if (full())expand();_allocator.construct(_last, std::move(val));_last++;
}

方式二:forward完美转化

//不管val传递的是左值还是右值,T&& val都能接收
//如果是左值,则接收后还是左值;反之右值,则还是右值
void construct(T* p, T&& val)//对象创建
{// 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733new (p) T(std::forward(val));//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void push_back(T&& val)
{if (full())expand();//虽然传入的val是一个右值引用变量,但是它本身是一个左值,//所以还是会调用void construct(T* p, const T& val)而不是带右值引用的函数_allocator.construct(_last, std::forward(val));_last++;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/106804.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端高频面试题 js中堆和栈的区别和浏览器的垃圾回收机制

一、 栈(stack)和 堆(heap) 栈(stack)&#xff1a;是栈内存的简称&#xff0c;栈是自动分配相对固定大小的内存空间&#xff0c;并由系统自动释放&#xff0c;栈数据结构遵循FILO&#xff08;first in last out&#xff09;先进后出的原则&#xff0c;较为经典的就是乒乓球盒结…

MathType7MAC中文版数学公式编辑器下载安装教程

如今许多之前需要手写的内容都可以在计算机中完成了。以前我们可以通过word输入一些简单的数学公式&#xff0c;但现在通过数学公式编辑器便可以完成几乎所有数学公式的写作。许多简单的数学公式&#xff0c;我们可以使用输入法一个个找到特殊符号并输入&#xff0c;但是对于高…

深度学习5:长短期记忆网络 – Long short-term memory | LSTM

目录 什么是 LSTM&#xff1f; LSTM的核心思路 什么是 LSTM&#xff1f; 长短期记忆网络——通常被称为 LSTM&#xff0c;是一种特殊的RNN&#xff0c;能够学习长期依赖性。由 Hochreiter 和 Schmidhuber&#xff08;1997&#xff09;提出的&#xff0c;并且在接下来的工作中…

4.12 TCP 连接,一端断电和进程崩溃有什么区别?

目录 TCP keepalive TCP 的保活机制 主机崩溃 进程崩溃 有数据传输的场景 客户端主机宕机&#xff0c;又迅速重启 客户端主机宕机&#xff0c;一直没有重启 TCP连接服务器宕机和进程退出情况总结 TCP keepalive TCP 的保活机制 TCP 保活机制需要通过 socket 接口设置 S…

Docker(md版)

Docker 一、Docker二、更换apt源三、docker搭建四、停启管理五、配置加速器5.1、方法一5.2、方法二 六、使用docker运行漏洞靶场1、拉取tomcat8镜像2、拉取成功3、开启服务4、查看kali的IP地址5、访问靶场6、关闭漏洞靶场 七、vulapps靶场搭建 一、Docker Docker是一个开源的应…

在Eclipse中创建javaweb工程

新建动态web工程 点击project或other之后&#xff0c;如何快速找到Dynamic Web Project 填写工程名等详细信息 也许会出现下面的对话框 项目结构图

界面组件DevExpress Reporting——增强的SQL和实体框架数据源引入

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 本文总结了v23.1中针对DevExpress报表和BI Das…

PROFIBUS主站转MODBUS TCP网关

1.产品功能 YC-DPM-TCP网关在Profibus总线侧实现主站功能&#xff0c;在以太网侧实现ModbusTcp服务器功能。可将Profibus DP从站接入到ModbusTcp网络&#xff1b;通过增加DP/PA耦合器&#xff0c;也可将Profibus PA从站接入ModbusTcp网络。YC-DPM-TCP网关最多支持125个Profibu…

【mysql是怎样运行的】-EXPLAIN详解

文章目录 1.基本语法2. EXPLAIN各列作用1. table2. id3. select_type4. partitions5. type 1.基本语法 EXPLAIN SELECT select_options #或者 DESCRIBE SELECT select_optionsEXPLAIN 语句输出的各个列的作用如下&#xff1a; 列名描述id在一个大的查询语句中每个SELECT关键…

软考:中级软件设计师:关系代数:中级软件设计师:关系代数,规范化理论函数依赖,它的价值和用途,键,范式,模式分解

软考&#xff1a;中级软件设计师:关系代数 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &…

R包开发1:RStudio 与 GitHub建立连接

目录 1.安装Git 2-配置Git&#xff08;只需配置一次&#xff09; 3-用SSH连接GitHub(只需配置一次) 4-创建Github远程仓库 5-克隆仓库到本地 目标&#xff1a;创建的R包&#xff0c;包含Git版本控制&#xff0c;并且能在远程Github仓库同步&#xff0c;相当于发布在Github。…

基于广义神经网络的网络入侵检测Matlab代码

1.案例背景 1.1 FCM 聚类算法 聚类方法是数据挖掘中经常使用的方法,它将物理的或抽象的对象分为几个种群,每个种群内部个体间具有较高的相似性,不同群体内部间个体相似性较低。模糊c均值聚类算法(Fuzzy C- Mean, FCM)是用隶属度确定每个元素属于某个类别程度的一种聚类算法&am…

卷积神经网络——下篇【深度学习】【PyTorch】【d2l】

文章目录 5、卷积神经网络5.10、⭐批量归一化5.10.1、理论部分5.10.2、代码部分 5.11、⭐残差网络&#xff08;ResNet&#xff09;5.11.1、理论部分5.11.2、代码部分 话题闲谈 5、卷积神经网络 5.10、⭐批量归一化 5.10.1、理论部分 批量归一化可以解决深层网络中梯度消失和…

深度学习经典检测方法的概述

深度学习经典的检测方法 two-stage&#xff08;两阶段&#xff09;&#xff1a;Faster-rcnn Mask-Rcnn系列 两阶段&#xff08;two-stage&#xff09;是指先通过一个区域提取网络&#xff08;region proposal network&#xff0c;RPN&#xff09;生成候选框&#xff0c;再通过…

k8s 常用命令(四)

12、删除pod中的nginx服务及service [rootmaster ~]# kubectl delete deployment nginx -n kube-public [rootmaster ~]# kubectl delete svc -n kube-public nginx-service 13、查看endpoint的信息 [rootmaster ~]# kubectl get endpoints 14、修改/更新&#xff08;镜像、…

react18+antd5.x(1):Notification组件的二次封装

antdesign已经给我们提供了很好的组件使用体验,但是我们还需要根据自己的项目业务进行更好的封装,减少我们的代码量,提升开发体验 效果展示 开起来和官网的使用没什么区别,但是我们在使用的时候,进行了二次封装,更利于我们进行开发 MyNotification.jsx,是我们的业务页面…

百度23Q2财报最新发布:营收利润加速增长,AI+生态战略渐显规模

百度集团-SW(9888.HK)Q2财报已于2023/08/22(美东)盘前发布&#xff0c;二季度百度集团整体收入实现341亿元&#xff0c;同比增长15%;归属百度的净利润(non-GAAP)达到80亿元&#xff0c;同比增长44%。营收和利润双双实现大幅增长&#xff0c;超市场预期。其中&#xff0c;百度核…

队列(Queue):先进先出的数据结构队列

栈与队列https://blog.csdn.net/qq_45467165/article/details/127958960?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127958960%22%2C%22source%22%3A%22qq_45467165%22%7D 队列&#xff08;Queue&#xff09;是一种常见的线…

CV:边缘检测的算法包含 Prewitt、Sobel、Laplacian 和 Canny。

目录 1. 边缘检测&#xff08;Prewitt&#xff09; 2. 边缘检测&#xff08;Sobel&#xff09; 3. 边缘检测&#xff08;Laplacian&#xff09; 3. 边缘检测&#xff08;Canny&#xff09; 边缘检测的算法包含 Prewitt、Sobel、Laplacian 和 Canny。 人在图像识别上具有难…

yolov3加上迁移学习和适度的数据增强形成的网络应用在输电线异物检测

Neural Detection of Foreign Objects for Transmission Lines in Power Systems Abstract. 输电线路为电能从一个地方输送到另一个地方提供了一条路径&#xff0c;确保输电线路的正常运行是向城市和企业供电的先决条件。主要威胁来自外来物&#xff0c;可能导致电力传输中断。…