当前位置: 首页 > article >正文

C++类和对象:匿名对象及连续构造拷贝编译器的优化

匿名对象和连续构造和拷贝时编译器的优化

  • github地址
  • 前言
  • 匿名对象:临时对象的妙用与优化
    • 1. 定义
    • 2. 核心特性解析
      • 2.1 生命周期管理
      • 2.2 创建方式对比
      • 2.3 匿名对象的引用特性
      • 2.4 匿名对象自动调用构造函数和析构函数。
    • 3. 匿名对象核心特性总结
      • 1. 本质特征
      • 2. 典型应用场景
      • 3. 与有名对象对比
  • 连续构造和拷贝时编译器的优化
    • 1️⃣ 何时会发生连续构造和拷贝?
    • 2️⃣ 编译器的优化技术:拷贝省略(Copy Elision)
      • 📌 **返回值优化(RVO, Return Value Optimization)**
      • 📌 具名返回值优化(NRVO, Named Return Value Optimization)
    • 3️⃣ 移动语义(C++11 之后)
    • 4️⃣ 优化条件与限制
    • 5️⃣ 如何最大化利用优化?
    • 6️⃣**验证优化效果**
    • 编译器优化的核心要点总结
      • 1. 核心优化机制
      • 2. 避坑指南

github地址

有梦想的电信狗

前言

本文介绍C++类和对象的细节:匿名对象和连续构造拷贝时编译器的优化

匿名对象:临时对象的妙用与优化

1. 定义

类型(实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象。

匿名对象(Anonymous Object)是指通过类型直接构造且未被命名的临时对象。其基本语法为:

ClassName(arguments);

2. 核心特性解析

2.1 生命周期管理

匿名对象在创建语句结束时立即析构:

class A {
public:
	A(int a = 0)
		:_a(a)
	{ 
		cout << "A(int a)" << endl;
	}
	~A() {
		cout << "~A()" << endl;
	}
private:
	int _a;
};
int main(){
	A();  		// 构造函数立即调用, 析构函数在此行结束时自动调用
	A a_1(1);	//有名对象 -- 生命周期在当前函数作用域
	A(2);		//匿名对象 -- 生命周期在当前行,即用即销毁
	return 0;
}
  • 有名非静态对象生命周期在当前函数作用域
  • 匿名对象生命周期在当前行,创建时调用构造函数,该行结束时调用析构函数。

2.2 创建方式对比

class Solution {
public:
	Solution(int x, int y) {
		cout << " Solution(int x, int y)" << endl;
	}
	Solution() {
		cout << " Solution()" << endl;
	}
public:
	int Sum_solution(int num) {
		cout << "Sum_solution()" << endl;
		return num;
	}
};
int main(){
	Solution s1;
	s1.Sum_solution(10);	//创建有名对象,并调用函数
	
	//用自己实现的构造函数创建匿名对象,
	Solution(10, 20).Sum_solution(20);	//匿名对象调用函数
	
	//用默认构造函数创建匿名对象, 
	Solution().Sum_solution(20);	//匿名对象调用函数
	return 0;
}

2.3 匿名对象的引用特性

  1. 匿名对象具有常性,因此需用const引用
  2. const引用的匿名对象,生命周期会被延长,在当前函数的作用域
int main(){
	//A& ra = A(1);		//匿名对象具有常性,因此需要用const引用
	const A& ra = A(1);		//const引用,延长匿名对象的生命周期,生命周期在当前函数作用域
	return 0;
}

2.4 匿名对象自动调用构造函数和析构函数。

在这里插入图片描述

3. 匿名对象核心特性总结

1. 本质特征

  • 无名临时性:通过 ClassName(args) 直接构造,无对象标识符
  • 极短生命周期:创建语句结束时立即析构,const引用可延长至函数作用域
    • 谨慎持有匿名对象引用
    const Data& ref = Data();  // 安全(生命周期延长)
    Data& ref = Data();        // ❌ 编译错误(非常引用)
    
  • 常性约束:需用 const 引用捕获(如 const A& ref = A(10);

2. 典型应用场景

  • 链式方法调用Solution().Sum(10);
  • 临时参数传递func(Data("temp.txt"));
  • 禁止长期持有:匿名对象析构后引用会悬空

3. 与有名对象对比

维度有名对象匿名对象
生命周期作用域结束前有效当前行结束即销毁
资源管理需手动控制析构时机自动析构,避免泄漏
使用场景需要重复访问一次性临时操作

连续构造和拷贝时编译器的优化

1️⃣ 何时会发生连续构造和拷贝?

假设有一个函数返回对象,代码可能隐含以下步骤:

class MyClass {
public:
    MyClass() { cout << "默认构造" << endl; }
    MyClass(const MyClass&) { cout << "拷贝构造" << endl; }
    MyClass(MyClass&&) { cout << "移动构造" << endl; }
};

MyClass create() {
    MyClass obj;
    return obj;  // 返回局部对象,会返回局部对象的拷贝。
}

int main() {
    MyClass a = create();  // 初始化 a
}

未优化时的执行流程

  1. create() 中构造 obj(默认构造)。
  2. return obj 时,将 obj 拷贝到临时对象(拷贝构造)。
  3. 临时对象再拷贝初始化 a(拷贝构造)。

总调用次数1 次默认构造 + 2 次拷贝构造。


2️⃣ 编译器的优化技术:拷贝省略(Copy Elision)

编译器会尽可能消除冗余的构造和拷贝操作,主要优化包括:

📌 返回值优化(RVO, Return Value Optimization)

  • 场景:函数返回一个 匿名临时对象
  • 效果:直接在调用处构造目标对象,跳过多余的临时对象。
MyClass create() {
    return MyClass();  // 匿名临时对象,如果没有RVO,会返回匿名对象的拷贝
}

MyClass a = create();  // RVO,直接构造 a,无拷贝

优化后调用次数:1 次默认构造。

📌 具名返回值优化(NRVO, Named Return Value Optimization)

  • 场景:函数返回一个 具名局部对象,可以理解为有名局部对象。
  • 效果:直接在调用处构造目标对象,跳过多余的拷贝。
MyClass create() {
    MyClass obj;       // 具名局部对象,无优化时,返回局部对象的拷贝
    return obj;        // NRVO 优化
}

MyClass a = create();  // 直接构造 a,无拷贝

优化后调用次数:1 次默认构造。


3️⃣ 移动语义(C++11 之后)

如果编译器无法应用拷贝省略(例如调试模式复杂控制流),C++11 的 移动语义 会进一步减少开销:

//编译器无法应用拷贝省略(例如`调试模式`或`复杂控制流`)
MyClass create() {
    MyClass obj;
    return obj;  // 优先尝试移动而非拷贝(若 NRVO 未优化)
}

// 若未优化:
// 1. obj 是局部对象,return 时触发移动构造(而非拷贝构造)。
// 2. 临时对象移动构造 a。

未优化时的调用次数:1 次默认构造 + 1 次移动构造。

在优化时(例如启用了 NRVO),编译器会将局部对象 obj 直接构造在函数返回值的存储位置,从而省略移动构造。此时:

优化时的调用次数:仅 1 次默认构造,移动构造被完全消除。


4️⃣ 优化条件与限制

优化类型触发条件可靠性
RVO返回匿名临时对象高度可靠
NRVO返回具名局部对象(单一路径返回)依赖编译器实现
移动语义对象有移动构造函数(若未优化则自动降级为移动)可靠

无法优化的情况

  • 函数返回全局变量或参数对象。
  • 函数内有多个返回路径且返回不同对象(如 if-else 分支返回不同具名对象)。

5️⃣ 如何最大化利用优化?

  1. 优先返回匿名临时对象(触发 RVO):
    MyClass create() {
        return MyClass();  // RVO 优化
    }
    
  2. 保持函数返回路径简单(提高 NRVO 成功率):
    MyClass create(bool flag) {
        if (flag) {
            MyClass obj1;
            return obj1;  // NRVO 可能失败
        } else {
            MyClass obj2;
            return obj2;  // NRVO 可能失败
        }
    }
    
  3. 启用 C++11 或更高标准(利用移动语义)。

6️⃣验证优化效果

在构造函数中插入打印语句,观察调用次数:

class MyClass {
public:
    MyClass() { cout << "默认构造" << endl; }
    MyClass(const MyClass&) { cout << "拷贝构造" << endl; }
    MyClass(MyClass&&) { cout << "移动构造" << endl; }
};

int main() {
    MyClass a = create();  // 观察输出
}
  • 优化后:仅输出 “默认构造”。
  • 未优化但启用移动语义:输出 “默认构造” + “移动构造”。

编译器优化的核心要点总结

1. 核心优化机制

技术触发场景优化效果可靠性
RVO返回匿名临时对象(return T()直接构造目标对象,0次拷贝所有编译器支持
NRVO返回具名局部对象(return obj;消除临时对象,1次构造依赖编译器实现
移动语义未优化时(C++11+)用移动代替拷贝,减少资源开销高可靠性

2. 避坑指南

  • 避免多返回路径
    // ❌ NRVO 可能失败
    Data create(bool flag) {
        Data d1, d2;
        return flag ? d1 : d2;  
    }
    

以上就是本文的所有内容了,如果觉得文章写的不错,还请留下免费的赞和收藏,也欢迎各位大佬在评论区交流

分享到此结束啦
一键三连,好运连连!


http://www.kler.cn/a/569810.html

相关文章:

  • Vscode通过Roo Cline接入Deepseek
  • 授权与认证之jwt(三)刷新令牌该如何设计
  • 计算机网络学习————(五)TCP/IP学习
  • ubuntu Linux 正确设置中文环境的方法
  • Python常见面试题的详解24
  • 实现用户特征自动识别和动态圈子创建,需构建一套完整的自动化流程
  • 本地搭建Ollama运行各种平台的大模型(deepseek),配合AnythingLLM-UI界面使用
  • 2025-02-28 学习记录--C/C++-PTA 7-34 通讯录的录入与显示
  • 基于消息事件实现结合状态机实现事件触发通用单片机逻裸机框架处理
  • 《机器学习数学基础》补充资料:线性变换和最小二乘
  • 【Go语言快速上手】第一部分:数据类型(数组、切片、映射)与控制语句
  • 【JAVA-数据结构】枚举
  • Storm实时流式计算系统(全解)——中
  • TCP的三次握⼿中为什么是三次?为什么不是两 次、四次?
  • 数据可视化02-PCA降维
  • 【推荐项目】023-游泳俱乐部管理系统
  • 解决Java项目中Maven爆红,三方包下载不下来的问题
  • 基于单片机和C#的电压监测系统设计
  • Docker 学习(一)
  • 如何在 FastAdmin 中实现自定义主题设计