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

C++11新特性大揭秘:优化性能与简化代码的利器

C++11当中的{}与传统的{}

C++11之前的{}初始化

传统的{}初始化(C++03以及之前)——传统C++中, {}主要用于聚合初始化,仅用于聚合类型。 聚合类型:

  • 结构体/类:无用户自定义构造函数、无基类、无非公有成员、无虚函数。
  • 数组:固定长度的原生数组。

示例:

代码语言:javascript

AI代码解释

// 结构体初始化 struct Point { int x; int y; }; Point p = {10, 20}; // 聚合初始化 // 数组初始化 int arr[] = {1, 2, 3}; // 枚举初始化(仅限C++11前部分编译器扩展) enum Color { Red, Green, Blue }; Color c = {Red};

限制:

  • 不能用于非聚合类型(如包含构造函数的类)。
  • 不支持动态容器(如 std::vector)的直接初始化。
C++11之后的 {} 初始化(列表初始化)

C++11引入了统一初始化语法(Uniform Initialization),扩展了 {} 的用途,使其适用于所有类型的初始化。

核心特性:

  • 适用所有类型:包括基本类型、类对象、STL容器等。
  • 防止窄化转换(Narrowing Conversion):禁止隐式截断(如 double → int 无显式转换时报错)。
  • 支持 std::initializer_list:允许类定义接受初始化列表的构造函数。 示例:

代码语言:javascript

AI代码解释

// 基本类型 int x{5}; // 替代 int x = 5; int y{}; // 值初始化(0) // 类对象 class Widget { public: Widget(int a, double b) { /* ... */ } }; Widget w1{10, 3.14}; // 调用构造函数 Widget w2{}; // 默认构造函数 // STL容器 std::vector<int> v{1, 2, 3}; // 初始化列表构造函数 std::map<int, std::string> m{{1, "a"}, {2, "b"}}; // 动态分配对象 int* ptr = new int[3]{1, 2, 3}; // 函数返回值 std::vector<int> create_vec() { return {1, 2, 3}; }

类成员初始化(C++11新增)

代码语言:javascript

AI代码解释

class MyClass { private: int a{10}; // 类内成员初始化 std::string s{"Hello"}; };

防止最令人烦恼的解析

代码语言:javascript

AI代码解释

Widget w1(); // 函数声明(传统问题) Widget w2{}; // 明确调用默认构造函数
C++11前后{}的比较

在这里插入图片描述

C++11中的std::initializer_list

std::initializer_list 的优先级 若类同时定义了接受 std::initializer_list 的构造函数和其他构造函数,编译器会优先匹std::initializer_list,可能导致意外行为:

代码语言:javascript

AI代码解释

std::vector<int> v1(5, 1); // 5个元素,每个为1 std::vector<int> v2{5, 1}; // 2个元素:5和1

空 {} 执行值初始化(Value Initialization):

  • 基本类型初始化为 0 或 nullptr。
  • 类类型调用默认构造函数。 自动类型推导 使用 auto 时需注意类型推导:

代码语言:javascript

AI代码解释

auto x{5}; // C++11中推导为 std::initializer_list<int> auto y = {5}; // 同上 auto z(5); // 推导为 int // C++17修复此问题:auto x{5} 推导为 int

示例代码对比:

代码语言:javascript

AI代码解释

// 传统初始化(C++03) int arr[] = {1, 2, 3}; Point p = {10, 20}; // C++11列表初始化 std::vector<int> nums{1, 2, 3}; Widget w{3, 2.7}; // 窄化转换错误 double d = 3.14; int a{d}; // 错误:窄化转换(C++11报错) int b(d); // 允许(但可能丢失精度)

C++11当中的右值引用

前言:C++98的C++语法中就有引⽤的语法,⽽C++11中新增了的右值引⽤语法特性,C++11之后我们之前学习的引⽤就叫做左值引⽤。⽆论左值引⽤还是右值引⽤,都是给对象取别名。

如何区别左值(Lvalue)与右值(Rvalue)

左值:有明确内存地址、可被取地址的表达式(如变量、函数返回的左值引用)。

代码语言:javascript

AI代码解释

int a = 10; // a 是左值 int& ref = a; // ref 是左值引用

右值:临时对象、字面量、表达式计算的中间结果(如 x + y、函数返回的非引用类型)。

代码语言:javascript

AI代码解释

42; // 字面量是右值 int&& rref = 10; // 右值引用绑定到字面量

值得⼀提的是,左值的英⽂简写为lvalue,右值的英⽂简写为rvalue。传统认为它们分别是left value、right value 的缩写。现代C++中,lvalue被解释为loactorvalue的缩写,可意为存储在内存中、有明确存储地址可以取地址的对象,⽽rvalue被解释为readvalue,指的是那些可以提供数据值,但是不可以寻址,例如:临时变量,字⾯量常量,存储于寄存器中的变量等,也就是说左值和右值的核⼼区别就是能否取地址。

右值引用语法右值引用通过 && 声明,只能绑定到右值。

代码语言:javascript

AI代码解释

int x = 10; int&& r1 = x; // 错误!不能绑定到左值 int&& r2 = x + 1; // 正确!绑定到临时结果(右值)

C++11右值引用核心工具:std::move(标记可移动对象)、std::forward(保持值类别)。

右值引用的核心作用:移动语义

问题背景:深拷贝的开销 传统 C++ 中,对象传递或赋值时默认使用 深拷贝。对于持有动态资源(如堆内存)的类,频繁拷贝会导致性能问题:

代码语言:javascript

AI代码解释

class BigData { public: BigData(size_t size) : data(new int[size]) {} ~BigData() { delete[] data; } // 拷贝构造函数(深拷贝) BigData(const BigData& other) : data(new int[other.size]) { std::copy(other.data, other.data + size, data); } private: int* data; }; BigData a(1000); BigData b = a; // 深拷贝:复制 1000 个元素(高开销)

移动构造函数与移动赋值运算符 右值引用允许定义 移动语义,直接将资源从临时对象“窃取”过来,避免深拷贝:

代码语言:javascript

AI代码解释

class BigData { public: // 移动构造函数 BigData(BigData&& other) noexcept : data(other.data) { // 直接接管资源 other.data = nullptr; // 原对象置空(避免重复释放) } // 移动赋值运算符 BigData& operator=(BigData&& other) noexcept { if (this != &other) { delete[] data; // 释放当前资源 data = other.data; // 接管资源 other.data = nullptr; } return *this; } private: int* data; }; BigData a(1000); BigData b = std::move(a); // 调用移动构造函数(零拷贝)
关键函数 std::move

std::move 将左值强制转换为右值引用,标记对象可被移动。 调用后,原对象处于“有效但未定义状态”(通常不再使用)。

代码语言:javascript

AI代码解释

BigData a(1000); BigData b = std::move(a); // a 的资源被转移给 b // 此时 a.data 为 nullptr
右值引用的核心作用:完美转发

问题背景:参数转发中的值类别丢失 模板函数转发参数时,若直接传递参数,可能丢失其原始值类别(左值/右值):

代码语言:javascript

AI代码解释

template<typename T> void wrapper(T arg) { func(arg); // arg 始终是左值,无法保持右值特性 } wrapper(10); // 传入右值,但 arg 变为左值

通用引用与 std::forward

  • 通用引用(Universal Reference):通过 T&& 声明的模板参数,可绑定到左值或右值。
  • std::forward 根据模板参数 T 的类型,保持参数的原始值类别。

代码语言:javascript

AI代码解释

template<typename T> void wrapper(T&& arg) { // 通用引用 func(std::forward<T>(arg)); // 完美转发 } int x = 10; wrapper(x); // 转发左值 wrapper(10); // 转发右值
右值引用的应用场景

优化容器操作 STL 容器(如 std::vector)利用移动语义减少元素拷贝:

代码语言:javascript

AI代码解释

std::vector<std::string> v; std::string s = "Hello"; v.push_back(s); // 拷贝构造(深拷贝) v.push_back(std::move(s)); // 移动构造(高效)

工厂函数返回对象 C++11 后,函数返回局部对象时,编译器会自动使用移动语义(若存在移动构造函数):

代码语言:javascript

AI代码解释

BigData createData() { BigData data(1000); return data; // 优先调用移动构造函数(而非拷贝) }

实现高性能智能指针 如 std::unique_ptr 的移动语义确保资源唯一所有权:

代码语言:javascript

AI代码解释

std::unique_ptr<int> p1 = std::make_unique<int>(10); std::unique_ptr<int> p2 = std::move(p1); // p1 变为 nullptr

引⽤折叠

C++中不能直接定义引⽤的引⽤如int& && r = i; 这样写会直接报错,通过模板或typedef中的类型操作可以构成引⽤的引⽤。

通过模板或typedef中的类型操作可以构成引⽤的引⽤时,这时C++11给出了⼀个引⽤折叠的规则:右值引⽤的右值引⽤折叠成右值引⽤,所有其他组合均折叠成左值引⽤。

下⾯的程序中很好的展⽰了模板和typedef时构成引⽤的引⽤时的引⽤折叠规则,⼤家需要⼀个⼀个仔细理解⼀下。

像f2这样的函数模板中,T&&x参数看起来是右值引⽤参数,但是由于引⽤折叠的规则,他传递左值时就是左值引⽤,传递右值时就是右值引⽤,有些地⽅也把这种函数模板的参数叫做万能引⽤。Function(T&&t)函数模板程序中,假设实参是int右值,模板参数T的推导int,实参是int左值,模板参数T的推导int&,再结合引⽤折叠规则,就实现了实参是左值,实例化出左值引⽤版本形参的Function,实参是右值,实例化出右值引⽤版本形参的Function.

http://www.jsqmd.com/news/733697/

相关文章:

  • ncmdump终极指南:3分钟解锁网易云音乐加密文件的完整解决方案
  • 1G/2.5G Ethernet PCS/PMA or SGMII IP核(五)
  • packer详解
  • 复杂地带的“生命方舟”:哈尔滨立和气垫船如何破解泥石流与湿地救援困局
  • 如何用Jasminum插件让Zotero中文文献管理效率提升90%
  • 亲测河南GEO厂家的体验居然这么真实? - 速递信息
  • ISO-Bench:AI生成代码性能评估基准测试实践
  • 微信小程序开发笔记
  • DEER-3D:错误驱动增强3D场景理解与编辑
  • EvolVE:LLM与进化算法结合的Verilog自动生成框架
  • 深度学习激活函数选择指南与实战对比
  • 2026年3月头部氢气去除技术服务推荐,氢气去除推荐,及时去除氢气防止泄漏 - 品牌推荐师
  • Deceive:3分钟实现游戏隐身,让你重新掌控在线隐私
  • 为什么87%的MCP 2026集成项目在UAT阶段失败?——基于12家头部客户日志的根因分析与48小时修复清单
  • 探秘InnoDB:搞懂它的内存、线程、磁盘与日志刷盘策略
  • 2026年大理正畸治疗机构TOP5出炉,口碑好的究竟有哪些? - 速递信息
  • SwiftUI API请求的加密之旅
  • springboot+vue|健身房管理系统(源码)
  • 3步开启多平台直播:obs-multi-rtmp插件完整使用指南
  • 2026年选太阳能路灯,认准这3家靠谱企业 - 速递信息
  • 小红书内容采集神器:三步搞定无水印批量下载,新手也能轻松上手
  • 【Tidyverse 2.0自动化报告终极指南】:零基础3天搭建可复用、可调度的R语言动态报表系统
  • LLM生成式优化的核心挑战与设计策略
  • 长春单招培训试听了几家,到底该怎么选? - 速递信息
  • NVIDIA显卡用户的福音:3步解决广色域显示器色彩过饱和问题
  • 数字孪生技术解析:从概念到智能交通与制造应用
  • 小微团队如何利用Taotoken统一管理多个项目的API密钥与访问
  • PvZ Toolkit:植物大战僵尸全能修改器,让你重新定义经典游戏体验
  • EmoCaliber:多模态情感理解框架的置信度表达机制
  • 长春单招培训亲测效果怎么样? - 速递信息