C++对象调用优化

news2024/12/29 9:18:05

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/132251733
    new (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/132251733
    new (p) T(val);//使用的是定位new,将val传到地址为p的空间去,p地址指向val
}
void construct(T* p, T&& val)//对象创建
{
    // 更多new初始化方法 https://blog.csdn.net/qq_45041871/article/details/132251733
    new (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/132251733
    new (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.coloradmin.cn/o/932347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION 论文精度笔记

DEFORMABLE DETR DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION 参考&#xff1a;AI-杂货铺-Transformer跨界CV又一佳作&#xff01;Deformable DETR&#xff1a;超强的小目标检测算法&#xff01; 摘要 摘要部分&#xff0c;作者主要说明了如…

STM32F4X Systick系统滴答定时器

STM32F4X Systick系统滴答定时器 Systick定时器Systick使用Systick时钟源Systick寄存器Systick频率计算Systick例程 Systick定时器 在以Crotex-M4为架构的MCU中&#xff0c;都会有一个Systick内核定时器&#xff0c;这个定时器的作用可以给系统一个心跳时钟&#xff0c;通常用…

python3/pip3 SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

环境&#xff1a; mac os 背景&#xff1a; 电脑之前安装的是python3.9 &#xff0c; 现在升级到python3.10。 从python官网下载macos版本的python3.10 pkg。 双击安装。 程序使用aiohttp访问ebay 。 出错&#xff1a; aiohttp.client_exceptions.ClientConnectorCertifi…

图论算法基础:最小生成树算法(kruskal算法和Prim算法)

文章目录 一.图邻接矩阵数据结构二.kruskal算法算法实现思想kruskal算法接口实现 三.Prim算法Prim算法接口实现 一.图邻接矩阵数据结构 以STLvector和unordered_map为适配容器实现图数据结构&#xff1a; namespace Graph_Structure {//Vertex是代表顶点的数据类型,Weight是边…

系统架构设计高级技能 · 大数据架构设计理论与实践

系列文章目录 系统架构设计高级技能 软件架构概念、架构风格、ABSD、架构复用、DSSA&#xff08;一&#xff09;【系统架构设计师】 系统架构设计高级技能 系统质量属性与架构评估&#xff08;二&#xff09;【系统架构设计师】 系统架构设计高级技能 软件可靠性分析与设计…

最新WAF信息收集技术

WAF信息收集 目前&#xff0c;市面上的WAF大多都部署了云服务进行防护加固&#xff0c;让WAF的防护性能得到进一步提升。 图1-32所示为安全狗最新版服务界面&#xff0c;增加了“加入服云”选项。 安全狗最新版服务界面&#xff0c;不仅加强了传统的WAF防护层&#xff0c;还增…

七层、四层和五层网络模型区别和联系

七层、四层和五层网络模型区别和联系 概述OSI网络7层模型&#xff08;概念型框架&#xff09;概述图片分析 四层模型概述常用协议OSI与TCP/IP四层的区别 五层模型概述三种网络模型对比 总结 概述 网络模型-七层模型&#xff08;OSI模型&#xff09;、五层协议体系结构和TCP/IP…

[C++ 网络协议] 多进程服务器端

具有代表性的并发服务器端实现模型和方法&#xff1a; 多进程服务器&#xff1a;通过创建多个进程提供服务。✔ 多路复用服务器&#xff1a;通过捆绑并统一管理I/O对象提供服务。 多线程服务器&#xff1a;通过生成与客户端等量的线程提供服务。 1. 进程的概念及应用 1.1 什么…

基于算术优化算法优化的BP神经网络(预测应用) - 附代码

基于算术优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于算术优化算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.算术优化优化BP神经网络2.1 BP神经网络参数设置2.2 算术优化算法应用 4.测试结果&#xff1a;5…

mybatis自动生成文件配置记录

记录下mybatis自动生成mapper文件&#xff0c;虽然现在有点过时了&#xff0c;但对于新手来说还是有一定用处的&#xff08;diss下通过这种文章引流关注的博主&#xff09;。 比较简单&#xff0c;基本就三步搞定&#xff01; 1、pom配置 <!--mybatis自动生成代码插件-->…

【Qt学习】07:绘图与绘图设备

OVERVIEW 绘图与绘图设备一、QPainter二、QPainterDevice1.QPixmap2.QBitmap3.QImage4.QPicture 绘图与绘图设备 一、QPainter Qt 的绘图系统允许使用API在屏幕和其它打印设备上进行绘制&#xff0c;整个绘图系统基于QPainter&#xff0c;QPainterDevice和QPaintEngine三个类&…

Eplan软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 Eplan软件是一款专为电气专业设计开发的软件&#xff0c;旨在提高电气设计的效率和质量。以下是Eplan软件的详细介绍。 1、Eplan的历史和演变 Eplan是一款由德国Eplan公司开发的电气设计软件&#xff0c;自1984年推出以来&…

如何在windows电脑上安装多个node,并可以进行随意切换

一、进入官网http://nvm.uihtm.com/ 下载 二、启动解压后的程序 1.开始安装nvm 选择要安装的目录 一直下一步–下一步–最后点击完成 3.最后点击完成即可 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3656568c7e9946e8a04219811fc4c4d3.png 三、在cmd控制台进行操作…

VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小

文章目录 VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小引言创建ESXi7.0可启动 U 盘结果检查VMware ESXi 7.0 优化VMFSL磁盘占用与系统存储大小 引言 本文讲述了在 J1900平台上安装ESXi7.0时减少 VMFSL 分区占用的说明, 通常这来说些主机内置的磁盘空间非常小, 采用默认安…

uniapp返回上一页并刷新

在uniapp中&#xff0c;经常会有返回上一页的情况&#xff0c;官方提供有 uni.navigateBack 这个api来实现效果&#xff0c;但是此方法返回到上一页之后页面并不会更新&#xff08;刷新&#xff09;。 例如有这样一个场景&#xff1a;从地址列表页点击添加按钮进入添加地址页面…

Aidex 移动端快速开发框架# RuoYi-Uniapp项目,uniapp vue app项目跨域问题

参考地址&#xff1a; manifest.json官方配置文档&#xff1a;manifest.json 应用配置 | uni-app官网 Chrome 调试跨域问题解决方案之插件篇&#xff1a; uni-app H5跨域问题解决方案&#xff08;CORS、Cross-Origin&#xff09; - DCloud问答 其实uni-app官方有解决跨域的办…

「MySQL-01」MySQL基础

目录 一、数据库概念 1. 什么是数据库 2. 为什么要有数据库&#xff1f; 3. 数据库将数据存在哪里&#xff1f; 二、知名数据库介绍 1.知名数据库介绍 2.为什么要学习MySQL 三、MySQL的基本使用 0. 安装MySQL 1. 数据库客户端链接服务端 2. Windows下的MySQL服务端管理 3. 数据…

Pygame编程(10)freetype模块

Pygame编程&#xff08;10&#xff09;freetype模块 函数示例 函数 pygame.freetype.get_error 返回最新的FreeType错误get_error() -> strget_error() -> None pygame.freetype.get_version 返回FreeType版本get_version(linkedTrue) -> (int, int, int) pygame.fre…

斯坦福人生设计课——简略笔记

来源&#xff1a;⽐尔博内特 戴夫伊万斯 著图书《人生设计课》 目录 一、认清当下的情况&#xff0c;从四个维度观察自己的人生 二、平衡人生&#xff0c;但不要走入误区 2.1 记录你的“美好时光日志”&#xff1a; 2.1.1 记录内容&#xff1a; 2.1.2 辅助反思的方法&…

C# 学习笔记--个人学习使用 <2>

C# 学习笔记 Chapter 2 比较硬的基础部分Section 1 委托Part 1 Action 与 func 委托的示例Part 2 自定义委托Part 3 委托的一般使用Part 4 委托的高级使用Part 5 适时地使用接口 Interface 取代一些对委托的使用 Section 2 事件Part 1 初步了解事件Part 2 事件的应用Part 3 事件…