一、C++11新特性概述

记忆口诀:C++11新特性,语法库扩双提升;auto decltype智能指,nullptr范围循环使;右值引用move效,无序容器正则到;Lambda匿名函数好,代码简洁效率高。

1. 语法改进

  • 统一初始化方法
  • 成员变量默认初始化
  • auto关键字:编译器自动推断类型
  • decltype:推导表达式类型
  • 智能指针:std::shared_ptr、std::unique_ptr
  • 空指针nullptr:替代NULL,类型明确
  • 基于范围的for循环:简化容器遍历
  • 右值引用和move语义:提高资源转移效率

2. 标准库扩充

  • 无序容器(哈希表):类似map但效率更高
  • 正则表达式:模式匹配字符串
  • Lambda表达式:定义匿名函数

二、智能指针

记忆口诀:智能指针分三类,shared_ptr共享随;引用计数来管理,线程安全要注意;unique_ptr独占权,禁止拷贝所有权;weak_ptr旁观态,lock检查免崩溃。

1. shared_ptr(共享指针)

  • 实现机制:基于引用计数,多指针共享同一资源
  • 核心组件:
    • 模板指针T* ptr:指向实际对象
    • 引用计数器:共享引用次数,决定资源释放时机
    • 重载操作符:*、->、=,支持指针操作
  • 线程安全:
    • 多线程读同一shared_ptr安全
    • 多线程写同一shared_ptr不安全
    • 多线程写共享引用计数的不同shared_ptr安全

2. unique_ptr(独占指针)

  • 独占所有权:同一时刻只能有一个unique_ptr指向对象
  • 自动销毁:离开作用域时自动销毁所指对象
  • 禁止拷贝:不支持普通拷贝和赋值操作
  • 所有权转移:可通过release或reset转移所有权

3. weak_ptr(弱引用指针)

  • 配合shared_ptr使用:观测资源使用情况
  • 不增加引用计数:不影响资源的生命周期
  • 检查有效性:使用lock()检查指针是否有效

三、类型推导

记忆口诀:auto编译推类型,必须初始化立即;函数参数成员变,数组模板不能见;decltype表达式,类型保留不执行;两者配合返回值,模板编程更灵活。

1. auto关键字

  • 编译期类型推导:必须立即初始化
  • 多变量声明限制:同一行变量类型推导不能有二义性
  • 使用限制:
    • 不能用作函数参数
    • 不能用作类的非静态成员变量
    • 不能定义数组(可定义指针)
    • 无法推导出模板参数
  • 引用和cv属性处理:
    • 非引用/指针声明:忽略引用和cv属性
    • 引用/指针声明:保留引用和cv属性

2. decltype关键字

  • 表达式类型推导:不执行表达式,仅分析类型
  • 引用和cv属性:保留表达式的引用和cv属性
  • 推导规则:
    • 表达式:与表达式类型相同
    • 函数调用:与函数返回值类型相同
    • 左值表达式:返回左值引用类型

3. auto与decltype配合使用

1
2
3
4
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
return t + u;
}

四、右值引用与移动语义

记忆口诀:左值有名可取址,右值无名不可指;将亡值来资源转,移动语义效率显;完美转发std::forward,参数原样不改变;深浅拷贝要分清,移动构造性能升。

1. 左值与右值

  • 左值:可放等号左边,可取地址,有名称
  • 右值:不可放等号左边,不可取地址,无名称
  • 特殊情况:
    • 字符串字面值"abcd"是左值
    • ++i、--i是左值,i++、i--是右值

2. 将亡值

  • 定义:即将销毁的值,可通过"盗取"内存获取
  • 优化作用:避免内存释放和分配,延长值的生命周期
  • 应用场景:完成移动构造或移动赋值操作

3. 引用类型

  • 左值引用:对左值的引用,必须立即初始化
  • 右值引用:对右值的引用,可用std::move强制转换左值

4. 移动语义

  • 本质:资源所有权转移,非拷贝
  • 实现:通过移动构造函数和std::move实现
  • 适用范围:仅对实现了移动构造函数的类有效
  • 浅拷贝与深拷贝:
    • 浅拷贝:指针指向同一块内存
    • 深拷贝:重新分配内存存储资源

5. 完美转发

  • 定义:将函数实参原样转发给其他函数
  • 实现:通过std::forward()函数模板实现

五、范围for循环与列表初始化

记忆口诀:范围循环真方便,变量冒号对象连;列表初始化用花括,信息丢失编译器说;代码简洁又安全,C++11特性添。

1. 范围for循环

  • 语法:for(变量:对象) 表达式
  • 应用场景:
    • 遍历string的每个字符
    • 遍历vector等容器元素

示例:

1
2
3
4
std::vector<int> arr(5, 100);
for (auto &i : arr) {
std::cout << i << std::endl;
}

2. 列表初始化

  • 语法:使用花括号{}进行初始化
  • 安全性:内置类型初始化时,若存在信息丢失风险,编译器报错

示例:

1
2
3
4
int x = 0;      // 传统初始化
int x = {0}; // 列表初始化1
int x{0}; // 列表初始化2
int x(0); // 构造函数初始化

六、Lambda表达式

记忆口诀:Lambda表达式,匿名函数便;方括捕获列表,圆括参数见;返回类型可省略,函数体在花括号间;捕获方式多种选,引用值捕获灵活变;STL算法结合用,代码简洁效率显。

1. 语法结构

1
2
3
[capture list] (parameter list) -> return type {function body}
// [捕获列表] (参数列表) -> 返回类型 {函数体}
// 必选部分:捕获列表和函数体

2. 变量捕获方式

  • []:不捕获任何变量
  • [&]:引用方式捕获所有变量
  • [=]:值方式捕获所有变量(创建时拷贝)
  • [=, &foo]:引用捕获foo,其他值捕获
  • [&, foo]:值捕获foo,其他引用捕获
  • [bar]:值方式捕获bar,不捕获其他变量
  • [this]:捕获所在类的this指针

3. 可变Lambda

  • 添加mutable关键字:允许修改值捕获的变量

4. STL算法中的应用

  • 优势:简化STL算法中谓词函数的使用
  • 示例:自定义排序规则
1
2
3
int arr[] = {6, 4, 3, 2, 1, 5};
// Lambda形式降序排序
std::sort(arr, arr + 6, [](const int& a, const int& b){return a > b;});

七、异常处理机制

记忆口诀:异常处理三剑客,try-throw-catch要记牢;try块包裹风险代,throw抛出异常来;catch捕获处理它,类型匹配最重要;标准异常层次清,基类派生各不同;构造析构少抛异,RAII技术资源保;C++没有finally,智能指针来帮忙。

1. 传统错误处理

  • 终止程序:如assert,用户体验差
  • 返回错误码:需要手动查找对应错误

2. C++异常处理关键字

  • try:包裹可能抛出异常的代码块
  • throw:抛出异常,中断当前执行流程
  • catch:捕获并处理异常

3. 异常匹配原则

  • 类型决定匹配:异常类型匹配catch块类型
  • 最近匹配原则:调用链中最近的匹配catch块被执行
  • 对象拷贝:抛出异常时生成异常对象的拷贝
  • catch(...):捕获任意类型异常,但无法获知具体类型
  • 派生类匹配:可使用基类捕获派生类异常

4. 异常安全

  • 构造函数异常:可能导致对象不完整,析构函数不会被调用
  • 析构函数异常:可能导致资源泄漏
  • 资源管理:使用RAII技术确保资源正确释放

5. 标准库异常体系

  • std::exception:所有标准库异常的基类
  • std::bad_alloc:内存分配失败
  • std::bad_cast:类型转换失败
  • std::logic_error:逻辑错误基类
    • std::invalid_argument:无效参数
    • std::domain_error:参数超出定义域
    • std::length_error:容器长度超限
  • std::runtime_error:运行时错误基类
    • std::overflow_error:数值溢出
    • std::underflow_error:数值下溢
    • std::range_error:数值超出有效范围
  • std::out_of_range:访问容器越界

6. 无finally关键字

  • C++不支持finally关键字
  • 使用RAII技术代替finally功能

八、RAII技术

记忆口诀:RAII技术真是好,资源管理离不了;构造获取资源到,析构自动释放掉;智能指针管内存,文件锁连不用恼;异常发生也不怕,资源泄漏杜绝了。

1. 定义

  • 资源获取即初始化:通过对象生命周期管理资源
  • 目的:确保资源在适当的时机自动释放

2. 应用场景

  • 内存管理:使用智能指针(shared_ptr、unique_ptr)
  • 文件操作:使用文件流对象自动关闭文件
  • 锁管理:使用锁包装器(lock_guard、unique_lock)自动释放锁
  • 网络连接:自定义RAII类管理连接的建立和关闭

九、命名空间

记忆口诀:命名空间划地盘,避免冲突是关键;作用域符双冒号,成员访问要记好;嵌套命名多层包,using指令简化调;匿名空间文件限,内部链接属static。

1. 定义与使用

  • 定义:使用namespace关键字划分作用域
  • 访问:使用作用域解析运算符::访问成员

示例:

1
2
3
4
5
6
7
8
namespace MyNamespace {
int a = 10;
void func() {}
}

// 访问方式
MyNamespace::a = 20;
MyNamespace::func();

2. 嵌套命名空间

  • 命名空间可嵌套定义
  • 访问时使用多个作用域解析运算符

3. using指令

  • using namespace 命名空间:直接使用所有成员
  • using 命名空间::成员:只引入特定成员

4. 匿名命名空间

  • 无名称命名空间,成员作用域限制在定义文件内
  • 等价于添加static关键字,具有内部链接属性

十、内联函数

记忆口诀:内联函数inline,编译展开开销减;短小频繁调用好,代码体积可能添;相比宏定义安全,类型检查调试便;编译器优化有选择,长函数可能被忽略。

1. 定义与特点

  • 定义:使用inline关键字声明
  • 编译时展开:将函数调用替换为函数体
  • 减少调用开销:适合短小、频繁调用的函数
  • 增大代码体积:可能增加可执行文件大小

2. 与宏的区别

  • 类型检查:内联函数进行类型检查,宏不检查
  • 作用域:内联函数遵循C++作用域规则,宏作用域到文件结束
  • 调试:内联函数可调试,宏不可调试
  • 安全性:内联函数更安全,避免宏的运算符优先级问题

十一、不可变字符串类实现

记忆口诀:不可变字符串,内容创建不变样;引用计数来管理,拷贝赋值共享享;计数为零资源放,内存管理高效强;禁用拷贝有两法,delete私有各有长。

1. 设计要点

  • 不可变性:创建后内容不可修改
  • 引用计数:高效管理内存,实现复制和赋值

2. 核心功能

  • 构造函数:初始化字符串数据和引用计数
  • 拷贝构造:共享数据,增加引用计数
  • 赋值运算符:减少原引用计数,共享新数据
  • 析构函数:减少引用计数,计数为0时释放资源
  • 辅助方法:length()获取长度,c_str()获取C风格字符串

3. 禁用拷贝功能

  • C++11:使用=delete语法禁用拷贝构造和赋值
  • 早期C++:将拷贝构造和赋值声明为私有成员