【C++】:C++11详解 —— 入门基础

news/2025/3/17 17:25:38/

目录

C++11简介

统一的列表初始化

1.初始化范围扩展

2.禁止窄化转换(Narrowing Conversion)

3.解决“最令人烦恼的解析”(Most Vexing Parse)

4.动态数组初始化

5. 直接初始化返回值

总结

声明

1.auto 类型推导

2. decltype 类型推导

3. 尾置返回类型(Trailing Return Type)

4. nullptr 关键字

5.using 别名声明

6.默认和删除函数(= default 与 = delete)

范围for循环

STL容器

1. std::array(固定大小数组)

2. std::forward_list(单向链表)

3. 无序关联容器(基于哈希表)

4. std::tuple(元组容器)


C++11简介

C++11(原称 C++0x)是 C++ 编程语言的第三个国际标准(ISO/IEC 14882:2011),于 2011 年 8 月正式发布。它是自 1998 年 C++98 标准后的首次重大更新,引入了大量新特性,旨在提高代码效率、简化开发流程并支持现代编程范式。C++11 被广泛认为是 C++ 的“现代化重生”,推动了语言向更高抽象层级、更安全的资源管理和更强大的并发支持发展。

C++11 的起源

  1. 应对现代编程挑战
    C++03(2003 年小修订版)后,开发者逐渐发现语言在并发编程、泛型编程和资源管理上的局限性。硬件多核化、移动语义需求(如高效对象转移)等趋势要求语言进化。

  2. 标准化进程

    • C++0x 项目:最初计划在 2000 年代完成(故代号 C++0x),但因复杂性多次延期,最终于 2011 年发布,更名为 C++11。

    • 委员会合作:ISO C++ 标准委员会(WG21)主导设计,融合了全球开发者、学术界和企业的提案,Bjarne Stroustrup(C++ 创始人)和 Herb Sutter 等专家推动关键特性。

  3. 设计目标

    • 保持与 C++98/C++03 的兼容性。

    • 提升类型安全性和性能(如右值引用、移动语义)。

    • 简化代码(如 auto 关键字、范围 for 循环)。

    • 支持多线程(标准线程库 <thread>)。

统一的列表初始化

C++11 引入了 统一列表初始化(Uniform Initialization),允许使用统一的 {} 语法初始化各种类型的对象(包括基本类型、数组、结构体、类对象、容器等)。这一特性解决了传统初始化方式的不一致性问题,增强了代码的可读性和安全性。

1.初始化范围扩展

C++98 的限制

  • 仅支持聚合类型(如数组、无自定义构造函数的结构体/类)的列表初始化:
int arr[] = {1, 2, 3};        // 允许:数组struct Point 
{ int x;int y; 
};
Point p = {3, 4};             // 允许:聚合类
  • 不支持非聚合类型(如有构造函数的类或 STL 容器)直接使用 {} 初始化:
std::vector<int> v = {1, 2, 3}; // C++98 报错:不支持直接列表初始化

C++11 的改进

  • 支持所有类型(包括非聚合类、容器、用户自定义类型)的列表初始化: 
    // 直接初始化非聚合类(需定义接受 std::initializer_list 的构造函数)std::vector<int> v = {1, 2, 3};   // 合法std::string s{"Hello"};           // 合法struct Point{int _x;int _y;};// 使用大括号对内置类型进行初始化int x1 = { 1 }; // 可添加等号int x2{ 2 };    // 可不添加等号// 使用大括号对数组元素进行初始化int array1[]{1, 2, 3, 4, 5}; // 可不添加等号// 使用大括号对结构体元素进行初始化Point p{ 1, 2 }; //可不添加等号

2.禁止窄化转换(Narrowing Conversion)

C++98 的问题

  • 允许隐式窄化转换(可能导致数据丢失或未定义行为):

int x = 5.5;   // 合法:隐式截断为 5(但可能导致逻辑错误)

C++11 的改进

  • 使用 {} 初始化时禁止隐式窄化转换:

int x{5.5};    // 编译错误:double → int 是窄化转换
int y(5.5);    // 合法(传统方式允许,但结果可能不符合预期)

3.解决“最令人烦恼的解析”(Most Vexing Parse)

C++98 的歧义问题

  • 声明对象时可能被误解析为函数声明:

class Widget { /*...*/ };
Widget w();     // 歧义:声明一个返回 Widget 的函数,而非默认构造对象

C++11 的改进

  • 使用 {} 消除歧义:

Widget w1{};    // 明确调用默认构造函数
Widget w2();    // 仍被解析为函数声明

4.动态数组初始化

C++98 的限制

  • 动态分配的数组无法直接初始化:

int* arr = new int[3]; // 需后续逐个赋值
arr[0] = 1; arr[1] = 2; arr[2] = 3;

C++11 的改进

  • 允许动态数组直接使用列表初始化:

int* arr = new int[3]{1, 2, 3}; // 合法

5. 直接初始化返回值

C++98 的局限

  • 函数返回复杂对象时需显式构造:

std::vector<int> getData() {std::vector<int> tmp;tmp.push_back(1);tmp.push_back(2);return tmp;
}

C++11 的改进

  • 可直接返回初始化列表:

std::vector<int> getData() {return {1, 2, 3}; // 直接构造并返回
}

总结

C++11 的统一列表初始化通过以下方式彻底改变了 C++ 的编码风格:

  1. 语法一致性:所有类型初始化方式统一,减少记忆负担。

  2. 安全性增强:禁止窄化转换,避免潜在错误。

  3. 代码简洁性:简化容器和复杂对象的初始化流程。

  4. 现代编程支持:为后续标准(如 C++14/17)的改进奠定基础。

声明

C++11 在变量、函数、类等声明方面引入了多项重要改进,旨在提升代码简洁性、类型安全性和表达能力。

1.auto 类型推导

  • 功能:允许编译器自动推导变量类型,减少冗余的类型书写。

auto x = 42;                    // x → int
auto s = "Hello";               // s → const char*
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();          // it → std::vector<int>::iterator// C++98 必须显式写出类型
std::vector<int>::iterator it = vec.begin();

2. decltype 类型推导

  • 功能:获取表达式的类型,用于声明复杂类型或模板编程。

int x = 10;
decltype(x) y = 20;             // y 的类型为 inttemplate<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { // 推导返回值类型return a + b;
}

3. 尾置返回类型(Trailing Return Type)

  • 功能:将函数返回类型放在参数列表之后,提高可读性(尤其适用于模板函数)。

auto func(int a, double b) -> int; // 声明// 结合 decltype 推导返回值类型。
// Lambda 表达式(隐式使用尾置返回类型)。
template<typename T, typename U>
auto multiply(T t, U u) -> decltype(t * u) {return t * u;
}

4. nullptr 关键字

  • 功能:替代 NULL 或 0 表示空指针,避免与整数类型的歧义。

void func(int);
void func(char*);
func(nullptr); // 调用 void func(char*)
func(0);       // 调用 void func(int)// C++98
int* p = NULL; // C++98 中 NULL 通常是 0 的宏定义,类型不安全

5.using 别名声明

  • 功能:替代 typedef 定义类型别名,支持模板化。

using IntPtr = int*;          // 等价于 typedef int* IntPtr;
template<typename T>using Vec = std::vector<T>;   // 模板化别名(C++98 无法实现)
Vec<int> v;                   // 等价于 std::vector<int>

6.默认和删除函数(= default 与 = delete

  • = default:显式要求编译器生成默认函数(如构造函数、拷贝赋值运算符)。

class MyClass {
public:MyClass() = default;      // 显式生成默认构造函数
};
  • = delete:禁止编译器生成特定函数,或禁用某些重载。
class NonCopyable {
public:NonCopyable(const NonCopyable&) = delete; // 禁止拷贝
};
void func(int) {}
void func(double) = delete;   // 禁用 double 版本的重载

范围for循环

范围 for 循环是 C++11 引入的一项简化遍历操作的特性,它允许开发者以更简洁、直观的方式遍历容器、数组或其他可迭代对象的元素。

基本语法

for (声明 : 范围表达式) 
{// 循环体
}
  • 声明:定义一个变量,用于依次获取范围内的每个元素(通常使用 auto 推导类型)。

  • 范围表达式:可以是数组、STL 容器(如 vectorlist)、初始化列表,或任何支持 begin() 和 end() 成员/自由函数的对象。

  • 隐式依赖 begin() 和 end()

    • 范围表达式必须能通过 begin(范围表达式) 和 end(范围表达式) 获取迭代器(包括自由函数或成员函数)。

代码示例:

// 结合 auto 关键字,避免显式写出复杂类型:
std::vector<int> vec = {1, 2, 3};
for (auto& num : vec)     // 使用引用避免拷贝
{ num *= 2; // 可修改元素
}// 使用 const auto& 避免拷贝,同时防止修改元素
for (const auto& str : stringList) 
{std::cout << str << std::endl;
}

STL容器

C++11中新增了五个容器,分别是array、forward_list、unordered_map和unordered_set、tuple

1. std::array(固定大小数组)

  • 用途:替代传统的 C 风格数组,提供安全的边界检查、STL 兼容接口和值语义

  • 对比 C++98:C 风格数组无成员函数,且易因越界导致未定义行为。

#include <array>
std::array<int, 3> arr = {1, 2, 3};
arr.at(1) = 42;    // 安全访问(越界抛出异常)
for (auto num : arr) { std::cout << num; } // 范围 for 循环

2. std::forward_list(单向链表)

  • 用途:内存敏感场景下的单向链表,仅支持前向遍历。

  • 特点

    • 比 std::list(双向链表)更省内存(每个节点少一个指针)。

    • 仅支持 push_front()insert_after() 等操作,无 size() 方法。

  • 适用场景:需要低内存开销且无需反向遍历的场景(如缓存池、轻量级链表)。

#include <forward_list>
std::forward_list<int> flist = {1, 2, 3};
flist.push_front(0);       // 头部插入
auto it = flist.begin();
flist.insert_after(it, 99); // 在第一个元素后插入

3. 无序关联容器(基于哈希表)

C++11 引入了基于哈希表的无序容器,提供平均 O(1) 复杂度的查找、插入和删除操作。

  • std::unordered_set 和 std::unordered_multiset

  • std::unordered_map 和 std::unordered_multimap

可以查看前面文章,STL详解

4. std::tuple(元组容器)

  • 用途:存储任意类型组合的固定大小集合(类似结构体,但无需显式定义类型)。

  • 特点

    • 支持通过 std::get<索引> 或结构化绑定(C++17)访问元素。

    • 常用于函数多返回值或泛型编程。

#include <tuple>
auto data = std::make_tuple(42, "Hello", 3.14);
int num = std::get<0>(data);      // 获取第一个元素
std::string str = std::get<1>(data); 

对比表格:C++11 新增容器 vs C++98 容器

容器类型C++11 新增C++98 类似容器主要差异
固定数组std::arrayC 风格数组安全、支持 STL 接口
单向链表std::forward_liststd::list仅前向遍历,内存更省
哈希集合std::unordered_setstd::set无序 vs 有序,哈希表 vs 红黑树
哈希映射std::unordered_mapstd::map同上
元组std::tuple(增强)无直接等价多类型混合存储

http://www.ppmy.cn/news/1579860.html

相关文章

优化VsCode终端样式

优化VsCode终端样式 打开vsCode&#xff0c;找到设置打开vsCode&#xff0c;找到设置&#xff0c;搜索 workbench&#xff08;中文&#xff09;找到 外观--> 点击seeting.json编辑根据自己的喜好选择主题&#xff0c; 打开vsCode&#xff0c;找到设置 打开vsCode&#xff0c…

ETIMEDOUT 网络超时问题

根据日志显示&#xff0c;你遇到的 ​**ETIMEDOUT 网络超时问题** 是由于 npm 无法连接到企业内部的 Nexus 仓库&#xff08;http://192.168.55.12:8001&#xff09;导致的。以下是具体原因和解决方案&#xff1a; 一、问题根源 ​Nexus 仓库不可达 日志中所有依赖包均尝试从 h…

SSL/TLS 1.2过程:Client端如何验证服务端证书?

快速回顾非对称加密和对称加密 首先快速说一下非对称加密和对称加密。非对称加密&#xff0c;就是有一个公钥和私钥(成对存在)。 公钥对一段文本A加密得到文本B&#xff0c;只有对应的私钥能对B解密得到A。 私钥对一段文本C加密得到文本D&#xff0c;只有对应的公钥能对D解密得…

数据结构——环形数组

环形数组 start 指向第一个有效元素的索引&#xff0c;end 指向最后一个有效元素的下一个位置索引。 注意&#xff1a; start是闭区间&#xff0c;先左移后赋值&#xff0c;先赋值(null)后右移&#xff1b;end是开区间&#xff0c;先赋值再右移&#xff0c;先左移再赋值(null…

T2.小牛架炮 - 美团机试真题题解

题目描述 在无限大的棋盘中有n个炮&#xff0c;第个炮的坐标是(xi,yi)。 已知每个炮的攻击方式是:先选一个攻击方向(上、下、左、右&#xff09;&#xff0c;该方向上看见的第一个棋子为“炮架”&#xff0c;该炮可以通过炮架攻击到炮架后面的棋子(只能攻击到炮架后面的第一个…

[JAVASE] Collection集合的遍历

一. 集合分类 java中的Collection集合分为两类, 分别是单列集合(List)和双列(Map)集合. 1.1 单列集合 1.2 双列集合 二. 集合遍历 2.1 List单列集合的遍历 for each遍历 迭代器遍历 lambda遍历 2.2 Map双列集合的遍历 for each遍历 k-v整体遍历 lambda表达式遍历

玩转github

me github 可以给仓库添加开发人员吗 4o 是的&#xff0c;GitHub允许仓库管理员为仓库添加开发人员&#xff0c;并设置这些开发人员的角色和权限。这里是一个简单的步骤指导&#xff0c;教你如何给一个 GitHub 仓库添加开发人员&#xff1a; 前提条件 你必须有这个仓库的权限&…

【蓝桥杯每日一题】3.16

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 目录 3.9 高精度算法 一、高精度加法 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a; 解题代码&#xff1a; 二、高精度减法 题目链接&#xff1a; 题目描述&…