目录
C++11是C++语言的一次重大更新,引入了众多新特性,使语言更现代化、更高效。以下是C++11主要新特性的详细整理:
auto主要作用是在变量声明时,依据初始化表达式自动推断变量类型。
void auto_test()
{
auto x = 42; //x 是 int 类型
auto y = 3.14; //y 是 double 类型
auto z = "hello"; //z 是 const char* 类型
}
decltype能够在编译时推导出表达式的类型,却不会对表达式进行求职。
void decltype_test()
{
int x = 42;
decltype(x) y = 10; //y是int 类型
decltype(x + y) z; //z是int 类型
}
based for循环
在C++中,基于范围的for循环是一种简化遍历序列(如容器、数组等)的语法,比传统的for循环更简洁。
for (declaration : range) {
// 循环体
}
- declaration:定义一个变量,用于存储每次迭代中的元素。
- range: 要遍历的对象(如数组、容器)。
智能指针(Smart Pointers)
unique_ptr(独占所有权)
特点
- 独占资源:同一时间只能有一个unique_ptr指向某个对象。
- 自动释放:当unique_ptr离开作用域时,所管理的对象会被自动删除。
- 不可复制:禁止拷贝构造和赋值,但可以通过std::move转移所有权
void unique_ptr_example()
{
// 创建 unique_ptr
std::unique_ptr<int> p1(new int(42));
// 移动语义
std::unique_ptr<int> p2 = std::move(p1); // p1 现在为空
// 访问指针
if (p2) { // 检查是否为空
std::cout << *p2 << std::endl; // 解引用
}
// 释放所有权
int* raw_ptr = p2.release(); // 需要手动 delete raw_ptr
// 重置指针
p1.reset(new int(200)); // 释放原对象,指向新对象
}
- reset(); // 自动删除管理的对象
- release(); // 不删除对象
shared_ptr(共享指针)
特点
- 共享所有权,多个shared_ptr可以指向同一对象
- 使用引用计数管理生命周期
- 当最后一个shared_ptr被销毁时释放内存
void shared_ptr_example()
{
//创建 shared_ptr
std::shared_ptr<int> p1 = std::make_shared<int>(42); //推荐方式
std::shared_ptr<int> p2(p1);//拷贝构造,引用计数+1
//访问指针
std::cout << *p1 << " use count: " << p1.use_count() << std::endl;
//重置指针
p1.reset();//引用计数-1
if(p1 == nullptr)
{
std::cout << " p1 is empty " << std::endl;
}
std::cout << *p2 << " use count: " << p2.use_count() << std::endl;
//自定义删除器
std::shared_ptr<FILE> file_ptr(fopen("test.txt","r"),fclose);
}
代码解析
std::shared_ptr<FILE> file_ptr(fopen("test.txt", "r"), fclose);
-
资源获取:
- fopen("test.txt","r") 打开文件,返回FILE* 类型指针
- 这是C标准库的文件操作函数
-
智能指针构造:
- 创建 shared_ptr<FILE> 对象
- 第一个参数是裸指针FILE*
- 第二个参数是删除器fclose
-
自动释放机制:
- 当引用计数归零时,会自动调用 fclose(file_ptr.get())
- 确保文件句柄一定会被关闭
weak_ptr(弱指针)
特点
- 不增加引用计数,解决shared_ptr循环引用问题
- 必须从shared_ptr创建
- 不能直接访问对象,需要先转换为shared_ptr
void weak_ptr_example()
{
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int> wp(sp);//创建 weak_ptr
//检查对象是否存在
if(!wp.expired())
{
//转换为shared_ptr访问
std::shared_ptr<int> sp2 = wp.lock();
std::cout << *sp2 << std::endl;
}
//另一种访问方式
try{
std::shared_ptr<int> sp3 = wp.lock();
if(sp3){
std::cout << *sp3 << std::endl;
}
}catch (const std::bad_weak_ptr& e){
std::cerr << e.what() << std::endl;
}
}
std::weak_ptr::expired()是 C++ 智能指针中用于检查对象生命周期状态的关键方法,它专门用于判断与weak_ptr关联的对象是否已被销毁。std::weak_ptr::lock()是 C++ 智能指针中最重要的方法之一,用于安全地访问由weak_ptr观察的对象。它提供了从弱引用到强引用的安全转换机制。
循环引用问题
class Node {
public:
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev; // 这里用shared_ptr会导致循环引用
~Node() { std::cout << "Node destroyed\n"; }
};
// 创建循环引用
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->prev = node1; // 循环引用形成
即使外部不再使用这些节点,它们的引用计数永远不会归零,导致内存泄漏。
weak_ptr 的解决方案
class NodeFixed {
public:
std::shared_ptr<NodeFixed> next;
std::weak_ptr<NodeFixed> prev; // 改为weak_ptr
~NodeFixed() { std::cout << "Node destroyed\n"; }
};
// 使用weak_ptr后
auto node1 = std::make_shared<NodeFixed>();
auto node2 = std::make_shared<NodeFixed>();
node1->next = node2;
node2->prev = node1; // weak_ptr不会增加引用计数
Lambda 表达式
C++11 引入的 Lambda 表达式是一种匿名函数对象,它允许你在需要的地方定义轻量级的内联函数,无需显式命名。这极大地简化了代码,尤其在使用 STL 算法(如 std::for_each、std::sort)或需要临时函数对象的场景中更为明显。
基本语法
[捕获列表](参数列表) -> 返回类型 { 函数体 }
最简单的 Lambda
auto hello = [] { std::cout << "Hello Lambda"; };
hello(); // 调用
带参数的 Lambda
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 4); // 输出7
捕获列表详解
捕获列表决定了外部变量如何被 Lambda 访问:
| 捕获方式 | 效果 | 示例 |
|---|---|---|
[] | 不捕获任何外部变量 | [] { /*...*/ } |
[x] | 按值捕获x | [x] { return x+1; } |
[&x] | 按引用捕获x | [&x] { x = 0; } |
[=] | 按值捕获所有外部变量 | [=] { return x+y; } |
[&] | 按引用捕获所有外部变量 | [&] { ++x; ++y; } |
[this] | 捕获当前类的this指针 | [this] { m_data = 1; } |
[x, &y] | 混合捕获(x按值,y按引用) | [x,&y] { y += x; } |
捕获示例
int a = 1, b = 2;
auto lambda = [a, &b] {
std::cout << a; // 访问副本
b++; // 修改外部b
// a++; // 错误!按值捕获默认const
};
mutable 关键字
默认情况下,按值捕获的变量是 const 的,使用 mutable 可以修改
int counter = 0;
auto increment = [counter]() mutable {
counter++; // 可以修改副本
return counter;
};
注意:mutable 不会影响外部原始变量,只影响 Lambda 内部的副本。
返回类型处理
自动类型推导
当 Lambda 体只有单个 return 语句时,返回类型可自动推导:
auto square = [](int x) { return x * x; }; // 返回int
显式指定返回类型
复杂情况需要显式指定:
auto divide = [](int a, int b) -> double {
if(b == 0) return 0.0;
return a / static_cast<double>(b);
};
Lambda 的实际应用
STL 算法中的 Lambda
std::vector<int> v = {1, 2, 3, 4, 5};
// 查找大于3的第一个元素
auto it = std::find_if(v.begin(), v.end(),
[](int x) { return x > 3; });
// 排序(降序)
std::sort(v.begin(), v.end(),
[](int a, int b) { return a > b; });
作为回调函数
void async_operation(std::function<void(int)> callback) {
//...执行操作后
callback(42);
}
async_operation([](int result) {
std::cout << "Got result: " << result;
});
Lambda 表达式:[](int result) { ... }
- 定义了一个匿名函数作为回调
- 当
async_operation调用callback(42)时,此 Lambda 会被执行
线程任务
std::thread t([]{
std::cout << "Running in thread";
});
t.join();
注意事项
生命周期问题
std::function<void()> create_lambda() {
int x = 10;
return [&x] { std::cout << x; }; // 危险!x将失效
}
性能考虑
- 小 Lambda 通常会被内联优化
- 大 Lambda 或复杂捕获可能影响性能
与函数指针的转换
- 无捕获的 Lambda 可转换为函数指针
void (*func_ptr)(int) = [](int x) { /*...*/ };
右值引用和移动语义
右值引用和移动语义是 C++11 引入的最重要特性之一,它们共同解决了资源高效转移的问题,彻底改变了 C++ 资源管理的方式。
基本概念
左值VS右值
| 特性 | 左值 (lvalue) | 右值 (rvalue) |
|---|---|---|
| 定义 | 有持久身份的对象 | 临时对象或即将销毁的值 |
| 例子 | 变量名、解引用指针 | 字面量、函数返回的临时对象 |
| 可被取地址 | 是 | 否 |
| 生命周期 | 超出当前表达式 | 当前表达式结束即销毁 |
右值引用
- 语法:
T&& - 关键特性:
- 只能绑定到右值
- 标识资源可被安全"窃取"的对象
- 延长临时对象生命周期到引用作用域结束
int&& rref = 42; // 正确:字面量是右值
int x = 10;
int&& rref2 = x; // 错误:x是左值
int&& rref3 = std::move(x); // 正确:转为右值引用
关键工具
std:move
将左值转为右值引用,表示该对象可以被移动:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1的资源被移动到v2
std::forward
完美转发,保持参数原始的值类别:
template<typename T>
void wrapper(T&& arg) {
process(std::forward<T>(arg));
}
移动语义
移动语义是一种允许将资源(如动态内存、文件句柄等)从一个对象转移到另一个对象的机制,而无需进行昂贵的深拷贝操作。其核心思想是"资源所有权转移"而非"资源复制"。
移动构造函数
移动构造函数是 C++11 引入的特殊成员函数,它通过"窃取"已有对象资源而非拷贝的方式初始化新对象,是移动语义的核心实现机制。
基本语法
class MyClass {
public:
// 移动构造函数
MyClass(MyClass&& other) noexcept
: 成员1(std::move(other.成员1)),
成员2(std::move(other.成员2)) {
// 将other置于有效但可析构状态
}
};
核心特性
- 参数类型:必须是本类类型的右值引用
MyClass&& - 不分配新资源:直接接管源对象的资源
- 修改源对象:使源对象处于有效但可析构状态
- 异常安全:应标记为
noexcept(影响 STL 容器的行为)
实现示例
管理动态内存的类
class Buffer {
char* data;
size_t size;
public:
// 移动构造函数
Buffer(Buffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr; // 重要:防止other析构时释放内存
other.size = 0;
}
~Buffer() { delete[] data; }
};
包含多个成员的类
class Widget {
std::string name;
std::vector<int> values;
int* resource;
public:
// 移动构造函数
Widget(Widget&& other) noexcept
: name(std::move(other.name)), // 移动string
values(std::move(other.values)),// 移动vector
resource(other.resource) { // 接管指针
other.resource = nullptr; // 置空源对象指针
}
};
移动赋值运算符
移动赋值运算符是 C++11 引入的特殊成员函数,它通过"窃取"已有对象资源而非拷贝的方式实现对象间的资源转移,是移动语义的重要组成部分。
基本语法
class MyClass {
public:
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) { // 防止自赋值
// 释放当前资源
// 接管other的资源
// 将other置于有效状态
}
return *this;
}
};
核心特性
- 参数类型:必须是本类类型的右值引用
MyClass&& - 返回值:返回本类类型的左值引用
MyClass& - 资源处理:先释放当前对象资源,再接管源对象资源
- 异常安全:应标记为
noexcept(影响STL容器的行为) - 自赋值检查:必须处理自赋值情况
实现示例
管理动态内存的类
class Buffer {
char* data;
size_t size;
public:
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data; // 释放当前资源
data = other.data; // 接管资源
size = other.size;
other.data = nullptr; // 置空源对象
other.size = 0;
}
return *this;
}
~Buffer() { delete[] data; }
};
包含STL容器的类
class Widget {
std::string name;
std::vector<int> data;
public:
// 移动赋值运算符
Widget& operator=(Widget&& other) noexcept {
if (this != &other) {
name = std::move(other.name); // 移动赋值string
data = std::move(other.data); // 移动赋值vector
}
return *this;
}
};
线程支持库
原子操作
新的容器
无序容器(哈希表)
| 容器 | 描述 | 头文件 |
|---|---|---|
std::unordered_set | 唯一键的哈希集合 | <unordered_set> |
std::unordered_multiset | 允许重复键的哈希集合 | <unordered_set> |
std::unordered_map | 键值对的哈希映射 | <unordered_map> |
std::unordered_multimap | 允许重复键的哈希映射 | <unordered_map> |
std::set和std::unordered_set对比
| 特性 | std::set | std::unordered_set |
|---|---|---|
| 头文件 | <set> | <unordered_set> |
| 底层实现 | 红黑树(平衡二叉搜索树) | 哈希表 |
| 元素排序 | 自动按键值排序(默认升序) | 无序存储 |
| 查找复杂度 | O(log n) | 平均O(1),最坏O(n) |
| 插入复杂度 | O(log n) | 平均O(1),最坏O(n) |
| 内存占用 | 较低(仅存储必要指针) | 较高(需维护哈希桶) |
| 迭代器稳定性 | 插入/删除不影响其他迭代器 | 插入可能导致重新哈希,使所有迭代器失效 |
| C++标准引入 | C++98 | C++11 |
固定大小数组
| 容器 | 描述 | 头文件 |
|---|---|---|
std::array | 固定大小的序列容器 | <array> |
特性:
- 替代传统C风格数组
- 知道自身大小(通过
size()成员函数) - 支持STL迭代器和算法
#include <array>
std::array<int, 5> arr = {1, 2, 3, 4, 5};
int size = arr.size(); // 5
单向链表
| 容器 | 描述 | 头文件 |
|---|---|---|
std::forward_list | 单向链表 | <forward_list> |

1439

被折叠的 条评论
为什么被折叠?



