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 匿名对象的引用特性
- 匿名对象具有常性,因此需用
const引用
。 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
}
未优化时的执行流程:
create()
中构造obj
(默认构造)。return obj
时,将obj
拷贝到临时对象(拷贝构造)。- 临时对象再拷贝初始化
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️⃣ 如何最大化利用优化?
- 优先返回匿名临时对象(触发 RVO):
MyClass create() { return MyClass(); // RVO 优化 }
- 保持函数返回路径简单(提高 NRVO 成功率):
MyClass create(bool flag) { if (flag) { MyClass obj1; return obj1; // NRVO 可能失败 } else { MyClass obj2; return obj2; // NRVO 可能失败 } }
- 启用 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; }
以上就是本文的所有内容了,如果觉得文章写的不错,还请留下免费的赞和收藏,也欢迎各位大佬在评论区交流
分享到此结束啦
一键三连,好运连连!