C++11新特性梳理

目录

based for循环

智能指针(Smart Pointers)

unique_ptr(独占所有权)

shared_ptr(共享指针)

weak_ptr(弱指针)

 循环引用问题

weak_ptr 的解决方案

Lambda 表达式 

基本语法 

最简单的 Lambda

带参数的 Lambda 

捕获列表详解

捕获示例 

mutable 关键字

返回类型处理 

自动类型推导

显式指定返回类型 

Lambda 的实际应用 

STL 算法中的 Lambda

作为回调函数

线程任务 

注意事项 

生命周期问题

性能考虑​

与函数指针的转换

右值引用和移动语义

基本概念

左值VS右值

右值引用

关键工具

std:move

std::forward

移动语义

移动构造函数

基本语法

核心特性

实现示例

管理动态内存的类

包含多个成员的类

移动赋值运算符

基本语法 

核心特性 

实现示例

管理动态内存的类

包含STL容器的类

线程支持库 

原子操作

新的容器

无序容器(哈希表)

std::set和std::unordered_set对比

固定大小数组 

单向链表 

空指针常量

枚举类 


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);
  1. 资源获取​​:

    • fopen("test.txt","r") 打开文件,返回FILE* 类型指针
    • 这是C标准库的文件操作函数
  2. ​智能指针构造​​:

    • 创建 shared_ptr<FILE> 对象
    • 第一个参数是裸指针FILE*
    • 第二个参数是删除器fclose
  3. ​自动释放机制​​:

    • 当引用计数归零时,会自动调用 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_eachstd::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置于有效但可析构状态
    }
};
核心特性
  1. ​参数类型​​:必须是本类类型的右值引用 MyClass&&
  2. ​不分配新资源​​:直接接管源对象的资源
  3. ​修改源对象​​:使源对象处于有效但可析构状态
  4. ​异常安全​​:应标记为 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;
    }
};
核心特性 
  1. ​参数类型​​:必须是本类类型的右值引用 MyClass&&
  2. ​返回值​​:返回本类类型的左值引用 MyClass&
  3. ​资源处理​​:先释放当前对象资源,再接管源对象资源
  4. ​异常安全​​:应标记为 noexcept(影响STL容器的行为)
  5. ​自赋值检查​​:必须处理自赋值情况
实现示例
管理动态内存的类
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::setstd::unordered_set
​头文件​<set><unordered_set>
​底层实现​红黑树(平衡二叉搜索树)哈希表
​元素排序​自动按键值排序(默认升序)无序存储
​查找复杂度​O(log n)平均O(1),最坏O(n)
​插入复杂度​O(log n)平均O(1),最坏O(n)
​内存占用​较低(仅存储必要指针)较高(需维护哈希桶)
​迭代器稳定性​插入/删除不影响其他迭代器插入可能导致重新哈希,使所有迭代器失效
​C++标准引入​C++98C++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>

空指针常量

枚举类 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值