目录
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 的起源
-
应对现代编程挑战
C++03(2003 年小修订版)后,开发者逐渐发现语言在并发编程、泛型编程和资源管理上的局限性。硬件多核化、移动语义需求(如高效对象转移)等趋势要求语言进化。 -
标准化进程
-
C++0x 项目:最初计划在 2000 年代完成(故代号 C++0x),但因复杂性多次延期,最终于 2011 年发布,更名为 C++11。
-
委员会合作:ISO C++ 标准委员会(WG21)主导设计,融合了全球开发者、学术界和企业的提案,Bjarne Stroustrup(C++ 创始人)和 Herb Sutter 等专家推动关键特性。
-
-
设计目标
-
保持与 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++ 的编码风格:
-
语法一致性:所有类型初始化方式统一,减少记忆负担。
-
安全性增强:禁止窄化转换,避免潜在错误。
-
代码简洁性:简化容器和复杂对象的初始化流程。
-
现代编程支持:为后续标准(如 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 容器(如
vector
、list
)、初始化列表,或任何支持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::array | C 风格数组 | 安全、支持 STL 接口 |
单向链表 | std::forward_list | std::list | 仅前向遍历,内存更省 |
哈希集合 | std::unordered_set | std::set | 无序 vs 有序,哈希表 vs 红黑树 |
哈希映射 | std::unordered_map | std::map | 同上 |
元组 | std::tuple (增强) | 无直接等价 | 多类型混合存储 |