C++基础面试题 | 什么是C++的移动语义和完美转发?

2024-08-27 01:28

本文主要是介绍C++基础面试题 | 什么是C++的移动语义和完美转发?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

若待上林花似锦,出门俱是看花人。 - 《城东早春》(杨巨源)

2024.8.26

回答重点

移动语义(Move Semantics)和完美转发(Perfect Forwarding)都是C++11 引入的新特性。

  1. 移动语义实现了从传统的拷贝资源到转移将亡对象资源,减少了拷贝,提高了效率。
  2. 而完美转发实现了一个可以接收任意类型参数的函数,并通过forward将原始的参数类型转发给最匹配的下层函数,最大限度地提高效率。

移动语义(Move Semantics)

移动语义是C++11中引入的一种机制,它允许对象的资源(如内存、文件句柄等)从一个对象转移到另一个对象,而不需要复制资源**(从拷贝构造函数的拷贝资源到移动构造的转移资源)**。它可以避免不必要的复制操作,从而提高程序的效率。

移动语义的核心是两个特殊的成员函数:

  1. 移动构造函数:接受一个右值引用作为参数,用于接收一个即将被销毁的对象,从而获取其资源。
  2. 移动赋值运算符:同样接受一个右值引用,用于将一个即将被销毁的对象的资源转移给当前对象。

例如,如果你有一个包含动态分配内存的类,你可以这样实现移动构造函数和移动赋值运算符:

class A {
public:A(int size) : size_(size) {data_ = new int[size];}A(){}A(const A& a) {size_ = a.size_;data_ = new int[size_];cout << "copy " << endl;}A(A&& a) {this->data_ = a.data_;a.data_ = nullptr;cout << "move " << endl;}~A() {if (data_ != nullptr) {delete[] data_;}}int *data_;int size_;
};
int main() {A a(10);A b = a;A c = std::move(a); // 调用移动构造函数return 0;
}

如果不使用move实现移动语义,使用将亡值构造新对象时内部的资源没有得到充分利用,拷贝代价极大。

std::vector<string> vecs;
...
std::vector<string> vecm = std::move(vecs); // 免去很多拷贝

完美转发(Perfect Forwarding)

完美转发是模板编程中的一个概念,它允许我们将函数的参数以它们原始的值类别(lvalue或rvalue)传递给另一个函数。这意味着我们可以在模板函数中接收一个参数,然后以它被传递给我们的方式传递给另一个函数,无论它是通过值、引用还是移动语义。

完美转发通常与std::forward一起使用。std::forward是一个函数模板,它接受一个通用引用(也称为转发引用),并将其转发给另一个函数。

例如,一个使用完美转发的模板函数可能看起来像这样:

#include<iostream>
using namespace std;
void PrintV(int &t) { // 接收被转发的左值对象cout << "lvalue" << endl;
}void PrintV(int &&t) { // 接收被转发的右值对象cout << "rvalue" << endl;
}// 定义一个模板函数 能够接收所有类型对象(左值和右值都会被传递至此)
template<typename T>
void Test(T &&t) {PrintV(std::forward<T>(t));// 变量t退化为左值// forward进行完美转发:如果原本的参数是右值,转化为右值传递到下一层// 如果原本的参数是左值,不做操作,正常传递到下一层。 下层函数printV根据参数决定调用的函数
}int main() {Test(1); // rvalueint a = 1;Test(a); // lvalueTest(std::forward<int>(a)); // rvalueTest(std::forward<int &>(a)); // lvalueTest(std::forward<int &&>(a)); // rvaluereturn 0;
}

这两个特性一起使用,可以极大地提高C++程序的效率和灵活性。

这篇关于C++基础面试题 | 什么是C++的移动语义和完美转发?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110238

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

C++读写word文档(.docx)DuckX库的使用详解

《C++读写word文档(.docx)DuckX库的使用详解》DuckX是C++库,用于创建/编辑.docx文件,支持读取文档、添加段落/片段、编辑表格,解决中文乱码需更改编码方案,进阶功能含文本替换... 目录一、基本用法1. 读取文档3. 添加段落4. 添加片段3. 编辑表格二、进阶用法1. 文本替换2

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转