C++_类和对象(下篇)—— 内部类、匿名对象、对象拷贝时的编译器优化

news/2024/9/19 0:12:17 标签: c++, 算法, c语言, 开发语言, 数据结构

目录

四、类和对象(下篇)

5、内部类

6、匿名对象 

7、对象拷贝时的编译器优化 


四、类和对象(下篇)

5、内部类

  1. 如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
  2. 内部类默认是外部类的友元类。
  3. 内部类本质也是⼀种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。B类受到A类域的作用影响与限制。
    #include<iostream>
    using namespace std;
    
    class A
    {
    private:
        static int _k;
        int _h = 1;
    
    public:
        class B // B默认就是A的友元
        {
        public:
            void foo(const A& a)
            {
                cout << _k << endl; //OK
                cout << a._h << endl; //OK
            }
        };
    };
    
    int A::_k = 1;
    
    int main()
    {
        cout << sizeof(A) << endl;
        A::B b;
        A aa;
        b.foo(aa);
    
        return 0;
    }

    思考:求1+2+3+...+n_牛客题霸_牛客网

上一篇博客C++_类和对象(中、下篇)中的一道牛客网站中的题目也可以使用内部类来实现:C++_类和对象(中、下篇)—— const成员函数、取地址运算符的重载、深入构造函数、类型转换、static成员、友元-CSDN博客

使用内部类的方法代码实现如下: 

class Solution 
{
    // 内部类
    class Sum
    {
    public:
        Sum()
        {
            _ret += _i;
            ++_i;
        }
    };

    static int _i;
    static int _ret;

public:
    int Sum_Solution(int n) 
    {
        // 变⻓数组
        Sum arr[n];
        return _ret;
    }
};

int Solution::_i = 1;
int Solution::_ret = 0;

        对比两种方法,内部类直接将Sum类转化为Solution类,使其成为一部分,可以直接通过友元关系直接调用,可访问对方的类,拥有权限访问。总的来说,C++的代码实现很少使用内部类这种方法。

6、匿名对象 

  1. 用类型(实参) 定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名(实参) 定义出来的叫有名对象。
  2. 有名对象的生命周期是在当前函数结束之后才会结束,与匿名对象的区别是生命周期的不同。
  3. 匿名对象生命周期只在当前一行,⼀般临时定义⼀个对象当前用⼀下即可,就可以定义匿名对象。
  4. 匿名对象有参或无参的时候一定要加上括号,不然无参的时候不加括号就会变成一个定义类。
    class A
    {
    public:
        A(int a = 0)
                :_a(a)
        {
            cout << "A(int a)" << endl;
        }
    
        ~A()
        {
            cout << "~A()" << endl;
        }
    
    private:
        int _a;
    };
    
    class Solution 
    {
    public:
        int Sum_Solution(int n) 
        {
            //...
            return n;
        }
    };
    
    int main()
    {
        A aa1;
    
        // 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义
        //A aa1();
    
        // 但是我们可以这么定义匿名对象,匿名对象的特点不⽤取名字,
        // 但是他的⽣命周期只有这⼀⾏,我们可以看到下⼀⾏他就会⾃动调⽤析构函数
        A();
        A(1);//匿名对象
    
        A aa2(2);
    
        //没用匿名对象之前,我们需要使用两行的代码来表示,如:
        //Solution s1;
        //cout << s1.Sum_Solution(10) << end1;
        //使用之后为:
        //cout << Solution().Sum_Solution(10) << end1;
    
        // 匿名对象在这样场景下就很好⽤,当然还有⼀些其他使⽤场景,这个我们以后遇到了再说
        Solution().Sum_Solution(10);
    
        return 0;
    }

    扩展:第一,某个函数给缺省值,使用匿名对象是一个很好的选择。

    void func(A aa = A(1))
    {}

    第二,匿名对象可以引用,但是它跟静态变量一样具有常性,要加上const来修饰,但是加上const会延长临时对象的生命周期,直到引用完(即跟着r变量来走),即构造之后马上析构,现用现销毁。

    const A& r = A()

7、对象拷贝时的编译器优化 

#include<iostream>
using namespace std;

class A
{
public:
    A(int a = 0)
            :_a1(a)
    {
        cout << "A(int a)" << endl;
    }

    A(const A& aa)
            :_a1(aa._a1)
    {
        cout << "A(const A& aa)" << endl;
    }

    A& operator=(const A& aa)
    {
        cout << "A& operator=(const A& aa)" << endl;

        if (this != &aa)
        {
            _a1 = aa._a1;
        }

        return *this;
    }

    ~A()
    {
        cout << "~A()" << endl;
    }

private:
    int _a1 = 1;
};

void f1(A aa)
{}

A f2()
{
    A aa;
    return aa;
}

int main()
{
    // 传值传参
    A aa1;
    f1(aa1);
    cout << endl;

    // 隐式类型,连续构造+拷⻉构造->优化为直接构造
    f1(1);

    // ⼀个表达式中,连续构造+拷⻉构造->优化为⼀个构造
    f1(A(2));
    cout << endl;

    cout << "***********************************************" << endl;

    // 传值返回
    // 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)
    // ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022 debug)
    f2();
    cout << endl;

    // 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)
    // ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022 debug)
    A aa2 = f2();
    cout << endl;

    // ⼀个表达式中,连续拷⻉构造+赋值重载->⽆法优化
    aa1 = f2();
    cout << endl;

    return 0;
}
  1. 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传返回值的过程中可以省略的拷贝。
  2. 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新⼀点的编译器对于连续⼀个表达式步骤中的连续拷贝会进行合并优化,有些更新更"激进"的编译器还会进行跨行跨表达式的合并优化。

 以下为Linux测试优化方面:

关闭优化后的直观结果:

VS2019的Debug版本优化:(比较老实)

 VS2022的Debug版本与其他版本的对比:(激进派)

编译结果的地址一致则证明是直接引用:


http://www.niftyadmin.cn/n/5664751.html

相关文章

【CSS】样式水平垂直居中

行内元素&#xff1a; 如果被设置元素为文本、图片等行内元素时&#xff0c;水平居中是通过给父元素设置 text-align:center <body> <div class"txtCenter">我想要在父容器中水平居中显示。</div> </body>div是文本元素的父元素 因此我们对…

C/C++语言基础--C++面向对象、类、对象概念讲解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 今天更新的比较晚了&#xff0c;主要一直用是谷歌Colab训练模型&#xff0c;访问国内csdn反而不好使了&#xff0c;请大家见谅&#xff1b;C是面向对象的语言&#xff0c;本文将介绍什么是面向对象、什么是类…

容器镜像同步工具image-migrator

1 概述 image-migrator是一个用于容器镜像同步的可执行二进制命令行工具&#xff08;不依赖于docker命令&#xff09;&#xff0c;能够自动将基于Docker Registry v2镜像仓库&#xff08;registry、云厂商容器镜像服务、docker hub、Quay、Harbor &#xff09;中的镜像迁移到基…

ShouldSniffAttr在自动化测试中具体是如何应用?

在自动化测试中&#xff0c;ShouldSniffAttr 这样的函数名通常暗示它是一个用于断言&#xff08;assertions&#xff09;的工具&#xff0c;用于检查某个元素或属性是否符合预期的条件。 虽然这不是一个标准的函数名&#xff0c;但我们可以根据命名推测其用途。 例如&#xf…

一种全新的webapi框架C#webmvc初步介绍

这个框架分三部分&#xff0c;第一部分数据结构层&#xff0c;第二部分http和业务管理以及sql层&#xff0c;第三部分加密层和工具类。 数据结构层分key和数据长度定义 public class Auth { [Key] public string Id { get; set; } [MaxLength(50)…

Ai+若依(智能售货机运营管理系统---帝可得)--货道关联商品【08篇---0004:关联商品】

货道关联商品 需求 对智能售货机内部的货道进行商品摆放的管理 此功能涉及四个后端接口 查询设备类型&#xff08;已完成&#xff09; 查询货道列表&#xff08;待完成&#xff09; 查询商品列表&#xff08;已完成&#xff09; 货道关联商品&#xff08;待完成&#xff0…

迈入IT世界:技术趋势、职业选择与未来展望

迈入IT世界&#xff1a;技术趋势、职业选择与未来展望 1. 引言 随着科技的飞速发展&#xff0c;信息技术&#xff08;IT&#xff09;已经成为当今社会的中坚力量。无论是智能设备、互联网服务&#xff0c;还是数据分析与人工智能&#xff0c;IT技术驱动着各行各业的创新与进步…

基于node.js koa2模拟快递柜存储取出快递微信小程序

本文介绍了一个基于Node.js Koa2框架的快递柜存储和取出快递的微信小程序。首先&#xff0c;我们使用Koa2框架搭建了一个简单的后端服务器&#xff0c;用于处理微信小程序发送的请求。然后&#xff0c;我们实现了快递柜的存储和取出功能&#xff0c;用户可以通过微信小程序扫描…