• 【C++】匿名对象 ③ ( 函数返回值为对象值时 匿名对象 的 拷贝构造函数 与 析构函数 调用情况分析 )






    一、匿名函数 与 拷贝构造函数




    1、匿名函数回顾


    在上一篇博客 【C++】匿名对象 ② ( 将 “ 匿名对象 “ 初始化给变量 | 将 “ 匿名对象 “ 赋值给变量 ) 中 , 分析了匿名函数的几种用法 , 以及不同的使用场景下 , 匿名对象 的 创建与销毁情况 ;


    C++ 编译器 发现 使用 匿名对象 时 , 会根据 匿名对象 的用法 , 决定对 匿名对象的 处理 ;

    • 匿名对象单独使用 : 如果只是单纯的使用 匿名对象 , 没有涉及到 将 匿名对象 赋值给其它变量 , 就会在表达式执行完毕后 , 销毁匿名对象 ;
    • 使用匿名对象初始化变量 : 如果 创建 匿名对象 后 , 还使用 匿名对象 初始化 变量 , 此时 编译器 会将 匿名对象 转为 普通对象 , 不会销毁该匿名对象 , 该对象会一直持续到该作用域结束 ;
    • 使用匿名对象为变量赋值 : 如果 创建 匿名对象 后 , 还使用 匿名对象 为 已存在的变量 赋值 , 此时 编译器 会将 匿名对象 的值赋值给 已存在的变量 , 并且立刻销毁该匿名对象 ;

    2、拷贝构造函数回顾


    博客中 , 分析了 拷贝构造函数 的调用时机 ;


    " 拷贝构造函数 " 又称为 " 赋值构造函数 " , 该类型构造函数有 4 种调用时机 ;

    • ① 使用一个对象初始化另外一个对象 : 使用 一个 类实例对象 初始化 另外一个 类实例对象 ;
    	// 使用一个对象初始化另外一个对象
    	// 直接手动 调用拷贝构造函数
    	Student s2 = Student(s1);
    
    • 1
    • 2
    • 3
    • ② 将一个对象赋值给另外一个对象 : 将 一个 类实例对象 赋值给 另外一个 类实例对象 ;
    	// 将一个对象赋值给另外一个对象
    	// 自动调用拷贝构造函数
    	Student s2 = s1;
    
    • 1
    • 2
    • 3
    • ③ 对象值作为函数参数 : 类的实例对象 以值的方式 传递给函数 , 不是以 指针 或 引用 的方式 ;
    // 定义函数, 接收 Student 对象值作为参数
    void fun(Student s)
    {
    }
    
    • 1
    • 2
    • 3
    • 4
    • ④ 对象值作为函数返回值 : 函数直接返回类的实例对象 值 , 不是返回 指针 或 引用 ;
    // 定义函数, 返回 Student 对象值作为返回值
    Student fun()
    {
    	Student s1(18, 170);
    	return s1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6




    二、当函数返回值为对象时的情况分析




    1、函数返回对象值时返回值为匿名对象


    如果一个 函数的返回值 是 类对象值 类型 , 不是 类对象的 引用 或 指针 类型 时 ,

    返回的 返回值 是一个 匿名对象 ;

    // 函数返回值是 Student 类型的对象
    Student fun()
    {
    	Student s(12, 190);
    	return s;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上述函数中执行的操作分析 :

    • 首先 , 调用 Student 类的 2 参数构造函数 , 创建 Student 类普通对象 , 初始化变量 s ;
    • 然后 , 返回 匿名对象 , 此时 调用 拷贝构造函数 , 将 普通对象 的值 拷贝给 匿名对象 ;
    • 再后 , 函数执行完毕 , 普通对象 需要被 销毁 , 此时调用析构函数 , 销毁 普通对象 ;

    2、处理 函数返回的匿名对象


    函数返回的匿名对象 有两种方案 :

    • 为 刚定义 变量 初始化 : 此时直接 将 匿名对象 转为 普通对象 ;
    • 为 已存在 变量 赋值 : 此时 将 匿名对象中的值取出 , 赋值给现有变量对象 , 匿名对象销毁 ;

    3、代码示例 - 函数返回的匿名对象 初始化 变量


    在下面的代码中 , fun 函数返回值是 Student 类型的匿名对象 ;

    // 函数返回值是 Student 类型的对象
    Student fun()
    {
    	Student s(12, 190);
    	return s;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在 main 函数中 , 调用该 fun 函数 , 将 返回的 匿名对象 用于初始化 变量 s ;

    	// 使用 函数返回匿名对象 初始化变量
    	Student s = fun();
    
    • 1
    • 2

    执行结果如下 :

    调用带参数构造函数 m_age = 12
    调用拷贝构造函数
    调用析构函数 : m_age = 12
    学生信息 : 年龄 = 12 , 身高 = 190
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5

    逐条分析 构造函数 / 拷贝构造函数 / 析构函数 的调用过程 :

    • 调用带参数构造函数 m_age = 12 这是在 fun 函数中 , 调用 有参构造函数 , 创建 普通对象 ;
    • 调用拷贝构造函数 这是在 fun 函数中 , 函数返回对象值时 , 创建 要返回的 普通对象副本 , 也就是一个 匿名对象 ;
    • 调用析构函数 : m_age = 12 这是 fun 函数执行完毕 , 在函数作用域中的 普通对象 需要被析构销毁 ;
    • 学生信息 : 年龄 = 12 , 身高 = 190 在 main 函数中 , 由于 将 匿名函数 直接用于初始化 变量 s , 因此直接将 匿名对象 转为 普通对象 , 这是调用普通对象的方法打印的日志 ;

    代码示例 :

    #include "iostream"
    using namespace std;
    
    class Student
    {
    public:
    
    	// 带参构造函数
    	Student(int age, int height)
    	{
    		m_age = age;
    		m_height = height;
    		cout << "调用带参数构造函数 m_age = " << m_age << endl;
    	}
    
    	// 打印学生信息
    	void printfInfo()
    	{
    		cout << "学生信息 : 年龄 = " << m_age  << " , 身高 = " << m_height << endl;
    	}
    
    	Student(const Student& s)
    	{
    		m_age = s.m_age;
    		m_height = s.m_height;
    		cout << "调用拷贝构造函数" << endl;
    	}
    
    	~Student()
    	{
    		cout << "调用析构函数 : m_age = " << m_age << endl;
    	}
    
    public:
    	int m_age;		// 年龄
    	int m_height;	// 身高
    };
    
    // 函数返回值是 Student 类型的对象
    Student fun()
    {
    	Student s(12, 190);
    	return s;
    }
    
    
    int main()
    {
    	// 使用 函数返回匿名对象 初始化变量
    	Student s = fun();
    
    
    	// 创建普通对象
    	//Student s(18, 180);
    		
    	// 函数返回匿名对象直接赋值给已存在的对象
    	//s = fun();
    
    
    	// 调用对象方法
    	s.printfInfo();
    
    
    	// 控制台暂停 , 按任意键继续向后执行
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    执行结果 :

    调用带参数构造函数 m_age = 12
    调用拷贝构造函数
    调用析构函数 : m_age = 12
    学生信息 : 年龄 = 12 , 身高 = 190
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述


    4、代码示例 - 函数返回的匿名对象 为 变量 赋值


    在下面的代码中 , fun 函数返回值是 Student 类型的匿名对象 ;

    // 函数返回值是 Student 类型的对象
    Student fun()
    {
    	Student s(12, 190);
    	return s;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在 main 函数中 , 调用该 fun 函数 , 将 返回的 匿名对象 用于 赋值给已存在的 变量 s ;

    	// 创建普通对象
    	Student s(18, 180);
    		
    	// 函数返回匿名对象直接赋值给已存在的对象
    	s = fun();
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行结果如下 :

    调用带参数构造函数 m_age = 18
    调用带参数构造函数 m_age = 12
    调用拷贝构造函数
    调用析构函数 : m_age = 12
    调用析构函数 : m_age = 12
    学生信息 : 年龄 = 12 , 身高 = 190
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    逐条分析 构造函数 / 拷贝构造函数 / 析构函数 的调用过程 :

    • 调用带参数构造函数 m_age = 18 这是在 main 函数中 , 调用 有参构造函数 , 创建 普通对象 ;
    • 调用带参数构造函数 m_age = 12 这是在 fun 函数中 , 调用 有参构造函数 , 创建 普通对象 ;
    • 调用拷贝构造函数 这是在 fun 函数中 , 函数返回对象值时 , 创建 要返回的 普通对象副本 , 也就是一个 匿名对象 ;
    • 调用析构函数 : m_age = 12 这是 fun 函数执行完毕 , 在函数作用域中的 普通对象 需要被析构销毁 ;
    • 调用析构函数 : m_age = 12 这是在 main 函数中 , 使用 匿名对象 为 普通变量赋值 , 需要将 匿名对象的值赋值给普通对象 , 匿名对象 之后直接销毁 , 这是调用析构函数 销毁 fun 函数返回的匿名对象 ;
    • 学生信息 : 年龄 = 12 , 身高 = 190 在 main 函数中 , 执行 被 匿名对象 赋值的 普通变量对象 的成员函数 ;

    代码示例 :

    #include "iostream"
    using namespace std;
    
    class Student
    {
    public:
    
    	// 带参构造函数
    	Student(int age, int height)
    	{
    		m_age = age;
    		m_height = height;
    		cout << "调用带参数构造函数 m_age = " << m_age << endl;
    	}
    
    	// 打印学生信息
    	void printfInfo()
    	{
    		cout << "学生信息 : 年龄 = " << m_age  << " , 身高 = " << m_height << endl;
    	}
    
    	Student(const Student& s)
    	{
    		m_age = s.m_age;
    		m_height = s.m_height;
    		cout << "调用拷贝构造函数" << endl;
    	}
    
    	~Student()
    	{
    		cout << "调用析构函数 : m_age = " << m_age << endl;
    	}
    
    public:
    	int m_age;		// 年龄
    	int m_height;	// 身高
    };
    
    // 函数返回值是 Student 类型的对象
    Student fun()
    {
    	Student s(12, 190);
    	return s;
    }
    
    
    int main()
    {
    	// 使用 函数返回匿名对象 初始化变量
    	//Student s = fun();
    
    
    	// 创建普通对象
    	Student s(18, 180);
    		
    	// 函数返回匿名对象直接赋值给已存在的对象
    	s = fun();
    
    
    	// 调用对象方法
    	s.printfInfo();
    
    
    	// 控制台暂停 , 按任意键继续向后执行
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    执行结果 :

    调用带参数构造函数 m_age = 18
    调用带参数构造函数 m_age = 12
    调用拷贝构造函数
    调用析构函数 : m_age = 12
    调用析构函数 : m_age = 12
    学生信息 : 年龄 = 12 , 身高 = 190
    Press any key to continue . . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

  • 相关阅读:
    2023年7月京东休闲食品行业品牌销售排行榜(京东大数据)
    Matlab中的特殊函数使用(合流超几何函数、Kummer函数、贝塞尔函数等)
    web3.0时代分布式网络协议的异同
    字节应届生薪资都能2万+,年薪30万,这样工作真的开心吗?
    第53期|GPTSecurity周报
    深入理解Kafka核心设计及原理(二):生产者
    Linux卷组管理
    EWM 分布式交货单部分过账回传ECC配置点
    【Python】组合数据类型
    @Transactional 竟也能解决分布式事务?
  • 原文地址:https://blog.csdn.net/han1202012/article/details/132902770