C++的对象优化经验

news2024/11/26 18:49:11

先看一个例子:

class Test{
private:
    int ma;

public:
    Test(int a = 0) : ma(a) { cout << "Test(int a)" << endl; }
    ~Test() { cout << "~Test" << endl; }
    Test(const Test &t)
    {
        ma = t.ma;
        cout << "Test(const Test&t)" << endl;
    }
    Test &operator=(const Test &t)
    {
        ma = t.ma;
        cout << "Test& operator=(const Test&t)" << endl;
    }
};

C++编译器对于对象的构造优化:用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就可以了,而不是先构造临时对象,然后拷贝构造给新定义的对象

eg:

    Test t2(t1);//拷贝构造
    Test t3=t1; //拷贝构造  因为t2 ,t3都是未定义的
    Test t4=Test();//和Test t4()是没有区别的,并不是先构造一个
                    //临时对象然后拷贝构造给t4

而对于已有对象进行构造赋值的时候,就是构造临时对象,然后调用赋值语句

eg:

    Test t3=t1; //拷贝构造  因为t2 ,t3都是未定义的

    t3=Test(20);   //构造临时,赋值

这个~Test就是产生的临时对象Test(20)的析构

eg:

显示生产临时对象,隐式生成临时对象

   Test t1;
    Test t2(t1);//拷贝构造
    Test t3=t1; //拷贝构造  因为t2 ,t3都是未定义的
    Test t4=Test();//和Test t4()是没有区别的,并不是先构造一个
                    //临时对象然后拷贝构造给t4

    t3=(Test)30;  //int ->Test(int) 显示
    t3=50;  //  int ->Test(int)  隐式  ,编译器都要看有没有Test(int)的构造函数

eg:

指针不要指向临时对象,因为临时对象会析构,指针指向一个已经析构的对象,这样不安全

引用可以指向临时对象,因为是别名

 Test *p=&Test(55);  //高版本编译器已经报错

    const Test &ps=Test(60);

所以指针这种都是要new一个出来,最后delete

    Test* p = new Test(55);
    delete p;

堆上分配的内存,不delete,是不会自动析构的,会泄漏内存

在实现string的时候对于拷贝构造和赋值,指针都需要都产生临时对象,不断的new delete效率很低,所以优化的时候引用右值拷贝,赋值

对于vector的push_back,你给他传左值,他就调左值的拷贝构造,你给他传右值,就会调右值的拷贝构造.vector怎么实现的呢?

ans:  在外面传的是用过模板类型推导和引用折叠+完美转发实现

注意右值引用变量其实是一个左值

int &&b=20;
int &c=b;

拷贝构造其实就是初始化的方式

什么是初始化的方式? 就是式子左边的还没有定义出来

xxx t1=已定义的或者匿名临时对象
xxx t1(已定义的或者匿名临时对象)

赋值方式就是,左边,右边都已经定义存在了

xxx t1
xxx  t2
t1=t2; 赋值

 emplace

像STL里面emplace系列的api都是通过可变参模板和引用折叠,forward实现的

 // emplace_back方法    
template <typename... Args>
    void emplace_back(Args&&... args) {
        if (size_ >= capacity_) {
            // 扩容
            reserve(capacity_ * 2);
        }
        // 在尾部直接构造对象
        new (data_ + size_) T(std::forward<Args>(args)...);
        ++size_;
    }

    // emplace方法
    template <typename... Args>
    void emplace(size_t pos, Args&&... args) {
        if (pos > size_) {
            throw std::out_of_range("Invalid position");
        }
        if (size_ >= capacity_) {
            // 扩容
            reserve(capacity_ * 2);
        }
        // 后移元素
        for (size_t i = size_; i > pos; --i) {
            data_[i] = std::move(data_[i-1]);
        }
        // 在指定位置直接构造对象
        new (data_ + pos) T(std::forward<Args>(args)...);
        ++size_;
    }

可变参模板部分补充

类模板的友元_模板友元

可变参数模板_c++可变参数模板类_右大臣的博客-CSDN博客

下面是对我这两个文章的一点补充

template<typename ...Args>
void add(Args&&... args)
{
    ((cout<<"args:"<<forward<Args>(args)<<endl),...);
}
int main()
{
    add(12,3,54,5);
    getchar();
    return 0;
}

 这里使用了折叠表达式 `(expression, ...)` 来展开参数包,并在每次展开时输出参数的值。这样,我们可以处理任意数量的参数,而不仅仅限于一个参数。

请注意,折叠表达式 `(expression, ...)` 是C++17引入的语法,它允许将一系列表达式以逗号分隔的形式展开。

平常我们自己写可能最多的是这么写的

template<typename T>
void add(T&& arg)
{
    std::cout<<"accept last args"<<endl;
    std::cout << arg << " ";
}

template<typename T, typename... Args> // 
void add(T&& arg, Args&&... args)
{
    std::cout << arg << " ";
    add(std::forward<Args>(args)...);  //forward不是必须的,这里只是做个完美转发
}
int main()
{
    add(12,3,54,5);
    getchar();
    return 0;
}

 第一个add函数是一个递归的终止条件,它用于处理参数包中只有一个参数的情况。当参数包中只剩下一个参数时,递归调用将停止,这时只剩下最后一个参数需要处理。

在这个例子中,第一个add函数接收一个右值引用参数T&& arg,并输出该参数的值。这个函数作为递归的终止条件,用于处理参数包中只有一个参数的情况。

然后,第二个add函数是一个递归函数模板,它接收一个右值引用参数T&& arg和一个参数包Args&&... args。它首先输出当前参数的值,然后使用递归调用add(std::forward<Args>(args)...),将剩余的参数包传递给下一次递归调用。

通过递归调用,这个函数模板可以依次处理参数包中的每个参数,直到参数包为空。这样可以实现对参数包中所有参数的输出。

因此,第一个add函数是为了处理参数包中只有一个参数的情况,而第二个add函数则用于递归地处理参数包中的多个参数。

当你add只给一个参数的时候就会调最上面那个add

    add(12);

模板元博大精深,后面这块只是对我之前写的一些博客的补充 

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

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

相关文章

【软件工程中的各种图】

1、用例图&#xff08;use case diagrams&#xff09; 【概念】描述用户需求&#xff0c;从用户的角度描述系统的功能 【描述方式】椭圆表示某个用例&#xff1b;人形符号表示角色 【目的】帮组开发团队以一种可视化的方式理解系统的功能需求 【用例图】 2、静态图(Static …

CXL Bias Mode (1) - Bias Mode 背景与分类

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c…

波奇学C++:实现一个简单的vector以及避开雷

vector的文档&#xff1a;vector - C Reference (cplusplus.com)​​​​​ 什么是vector&#xff1f; vector是数组。vector空间连续&#xff0c;可变大小&#xff0c;可存放自定义类型。 vector简单的使用 vector<int> v; //int型实例化 v.push_back(1);//插入数据 …

selenium定位rect元素

rect元素属性 rect元素的属性如下&#xff1a; x&#xff1a;此属性确定矩形的x坐标。 值类型&#xff1a;| ; 默认值&#xff1a;0 动画&#xff1a;是y&#xff1a;此属性确定矩形的y坐标。 值类型&#xff1a;| ; 默认值&#xff1a;0 动画&#xff1a;是width&#xff1a…

Web后端开发总结

后端web开发大致流程 和对应的核心技术 对应技术的来源 springMVC可以理解为spring框架中的web开发框架 springMVCSpringMybatis就是我们熟知的ssm框架了

【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(7 月 21 日论文合集)

文章目录 一、检测相关(15篇)1.1 Representation Learning in Anomaly Detection: Successes, Limits and a Grand Challenge1.2 AlignDet: Aligning Pre-training and Fine-tuning in Object Detection1.3 Cascade-DETR: Delving into High-Quality Universal Object Detectio…

K8S初级入门系列之十一-安全

一、前言 安全是K8S重要的特性&#xff0c;在K8S初级入门系列之四-Namespace/ConfigMap/Secret章节&#xff0c;我们已经已经了解了Namespace&#xff0c;Secret与安全相关的知识。本篇将梳理K8S在安全方面的策略。主要包括两个方面&#xff0c;API安全访问策略以及Pod安全策略…

C++ 之命名空间namespace【详解】

文章目录 一&#xff0c;命名空间出现的意义二&#xff0c;命名空间的定义命名空间里面可以包含变量&#xff0c;函数&#xff0c;类型&#xff1a;命名空间不可以定义在局部作用域&#xff1a;命名空间可以嵌套&#xff1a; 三&#xff0c;同一个工程中允许存在多个相同名称的…

vue-cli项目中,使用webpack-bundle-analyzer进行模块分析,查看各个模块的体积,方便后期代码优化

一、安装 npm install --save-dev webpack-bundle-analyzer 二、在vue.config.js中配置 const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin plugins: [new BundleAnalyzerPlugin({analyzerMode: server,analyzerHost: 127.0.0.1,analyze…

微信小程序原生上传图片和预览+云函数上传

1.前台页面 1.1wxml问阿金 <!-- 说明一个上传页面的按钮 --> <button type"primary" bindtap"uploadPage">上传页面展示</button> <!-- 声明一个上传服务器的按钮 --> <button type"warn" bindtap"uploadSeve…

【广州华锐互动】列车人员疏散VR虚拟演练系统

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐应用于各个领域。在火车站安全方面&#xff0c;为了提高旅客的安全意识和应对突发事件的能力&#xff0c;列车人员疏散VR虚拟演练系统应运而生。 列车人员疏散VR虚拟演练系统是一种基于虚拟现实技术的教育培训系统&…

进程信号的理解

进程信号 1. 信号的概念2. 信号的产生3. 信号的保存1. 信号其他相关常见概念2. 在内核中的表示3.信号集操作函数 4. 信号的处理&#xff08;捕捉&#xff09; 1. 信号的概念 信号的一生&#xff0c;进程信号从产生到被处理所经历的过程一共分成了三步&#xff1a;信号产生、信…

【C++】STL---list基本用法介绍

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

文件批量智能归类

在日常工作中我们会经常碰到同一个文件夹里面多个文件有视频&#xff0c;图片&#xff0c;或视频标题名称不一样&#xff0c;图片名称不一样&#xff0c;整个文件夹看下来很混乱&#xff0c;需要找一个文件工花费很长时间去找&#xff0c;一个一个用眼睛去看&#xff0c;看久眼…

Linux开发工具使用

Linux开发工具使用 vim1.vim的基本概念2.vim三种模式的切换3.底行模式的基础操作4.命令模式下的基础操作5.vim的配置 yum1.yum的概念2.yum的基础操作 gcc/g1.gcc/g的概念2.一个C/C程序形成的过程3.gcc/g基本使用 make和makefile1.基础概念2.makefile【1】生成【2】清理 调试器g…

2023国际高校数学建模竞赛B题三星堆文物原创论文讲解

大家好呀&#xff0c;从昨天发布赛题一直到现在&#xff0c;总算完成了国际高校数学建模竞赛B题完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论文共28页&#xff0c;一些…

docker php 容器安装redis和mongodb扩展

一、背景 很多项目(几乎所有)都有用到redis和mongodb来存储数据&#xff0c;php没有自带这些扩展&#xff0c;需要手动安装 二、PHP redis扩展安装步骤 这里以php8.2版本容器为例&#xff0c;以下命令中‘php82’均为容器名称&#xff0c;需要更换为你自己的实际名称&#x…

JZ31 栈的压入、弹出序列-C++

题目来源&#xff1a;牛客网 题目描述&#xff1a; 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序&#xff0c;序列4,5,3,2,1是该压栈序列…

List如何正确删除元素

public static void main(String[] args) {List<Integer> list Lists.newArrayList(1, 2, 3, 4, 5);list.forEach(item -> {if (item 3) {list.remove(3);}});} 使用foreach删除集合元素的时候&#xff0c;有可能会报错&#xff0c;报错信息如下&#xff1a; 这是因…

【docker】docker

目录 一、docker概念二、docker安装(centos7)三、docker架构3.1 镜像image3.2 容器container 四、配置docker镜像加速器五、docker命令5.1 docker服务命令5.2 docker镜像命令5.3 docker容器命令 六、docker容器的数据卷6.1 容器卷概念及作用6.2 配置数据卷6.3 挂载示例6.4 数据…