当前位置: 首页 > news >正文

智能指针、移动语义、完美转发、lambda

智能指针

C++中的智能指针是一种管理动态内存的工具,通过自动管理资源的生命周期,减少内存泄漏的风险。智能指针的原理是在对象上包装一个指针,并在智能指针离开作用域时自动释放资源。主要类型包括 std::unique_ptrstd::shared_ptrstd::weak_ptr,它们分别提供了不同的内存管理策略。

1. std::unique_ptr 实现原理

std::unique_ptr 是一种独占所有权的智能指针,每个对象在同一时间只能有一个 unique_ptr 指向它。当 unique_ptr 离开作用域或被显式销毁时,资源自动释放。

  • 实现原理

    • unique_ptr 使用普通指针管理动态分配的对象。
    • unique_ptr 不允许拷贝,但允许移动构造和移动赋值。通过禁用拷贝构造和拷贝赋值,确保了同一时间只有一个 unique_ptr 实例指向对象。
    • unique_ptr 离开作用域时,调用析构函数自动释放内存。
  • 简易实现示例

    template<typename T>
    class UniquePtr {T* ptr;
    public:explicit UniquePtr(T* p = nullptr) : ptr(p) {}~UniquePtr() { delete ptr; }UniquePtr(const UniquePtr&) = delete;UniquePtr& operator=(const UniquePtr&) = delete;UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) {other.ptr = nullptr;}UniquePtr& operator=(UniquePtr&& other) noexcept {if (this != &other) {delete ptr;ptr = other.ptr;other.ptr = nullptr;}return *this;}T* get() const { return ptr; }T& operator*() const { return *ptr; }T* operator->() const { return ptr; }
    };
    

2. std::shared_ptr 实现原理

std::shared_ptr 是一种允许多个指针共享对象所有权的智能指针,采用引用计数来管理资源。每当一个新的 shared_ptr 指向对象时,引用计数加一;当一个 shared_ptr 离开作用域时,引用计数减一。引用计数为零时,资源才会释放。

  • 实现原理

    • 通过一个控制块(Control Block)来管理引用计数,通常控制块包含一个引用计数器和一个指向对象的指针。
    • 在每次拷贝构造或赋值时,控制块的引用计数递增;在每次析构时,引用计数递减。
    • 当引用计数为零时,删除指针指向的对象并释放控制块的内存。
  • 简易实现示例

    template<typename T>
    class SharedPtr {T* ptr;int* count;
    public:explicit SharedPtr(T* p = nullptr) : ptr(p), count(new int(1)) {}SharedPtr(const SharedPtr& other) : ptr(other.ptr), count(other.count) {++(*count);}SharedPtr& operator=(const SharedPtr& other) {if (this != &other) {if (--(*count) == 0) {delete ptr;delete count;}ptr = other.ptr;count = other.count;++(*count);}return *this;}~SharedPtr() {if (--(*count) == 0) {delete ptr;delete count;}}T* get() const { return ptr; }T& operator*() const { return *ptr; }T* operator->() const { return ptr; }
    };
    

3. std::weak_ptr 实现原理

std::weak_ptr 是与 std::shared_ptr 配合使用的辅助智能指针,它提供对对象的非拥有引用,不会增加引用计数。

  • 实现原理

    • weak_ptr 内部持有指向控制块的指针,可以观察但不管理对象生命周期。
    • shared_ptr 的引用计数为零时,weak_ptr 也变为无效,通过 expired()lock() 检查对象是否存在。
    • lock() 方法创建一个新的 shared_ptr,若对象还存在则返回有效的指针,否则返回 nullptr
  • 简易实现示例

    template<typename T>
    class WeakPtr {T* ptr;int* count;
    public:WeakPtr(const SharedPtr<T>& sp) : ptr(sp.ptr), count(sp.count) {}bool expired() const {return *count == 0;}SharedPtr<T> lock() const {if (expired()) {return SharedPtr<T>(nullptr);}return SharedPtr<T>(*this);}
    };
    

总结

  • unique_ptr:独占所有权,无拷贝。
  • shared_ptr:共享所有权,基于引用计数管理。
  • weak_ptr:弱引用,不影响对象生命周期。

右值引用

右值引用 (rvalue reference) 是 C++11 引入的一种引用类型,主要用于实现移动语义完美转发,从而提升程序性能并减少不必要的内存拷贝。

1. 什么是右值引用?

右值引用是一个新的引用类型,使用 T&& 表示,其中 T 是数据类型。它的主要用途是捕获右值,即临时对象不需要再使用的对象

  • 左值:在程序中有命名的实体,可以取地址。例如变量 x 或对象 myObj
  • 右值:不具有持久性、不可取地址的临时对象。例如字面值 10、表达式 x + y 的结果等。

示例:

int a = 10;        // a 是左值
int b = a + 5;     // (a + 5) 是右值,不会再被使用

2. 右值引用的用途

a. 实现移动语义

移动语义允许对象的资源(如动态内存)从一个对象“移动”到另一个对象,而不是进行深拷贝。使用移动语义可以显著提升性能,尤其是在大数据对象或容器的情况下。

#include <iostream>
#include <vector>class MyVector {
public:std::vector<int> data;// 普通构造函数MyVector(size_t size) : data(size) {}// 移动构造函数MyVector(MyVector&& other) noexcept : data(std::move(other.data)) {std::cout << "Move constructor called\n";}// 禁用拷贝构造函数MyVector(const MyVector&) = delete;
};int main() {MyVector v1(1000);             // 正常构造MyVector v2 = std::move(v1);   // 触发移动构造return 0;
}

在上面的例子中,std::move(v1)v1 转换为右值引用,并调用 MyVector 的移动构造函数,把 v1.data 的内容转移到 v2.data,避免了深拷贝。

b. 完美转发

完美转发允许函数模板将参数“完美地”传递给另一个函数,使其既能处理左值也能处理右值。右值引用搭配 std::forward 可以实现这种功能。

#include <iostream>
#include <utility>void process(int& x) {std::cout << "Lvalue reference\n";
}void process(int&& x) {std::cout << "Rvalue reference\n";
}template <typename T>
void forwarder(T&& arg) {process(std::forward<T>(arg));
}int main() {int a = 10;forwarder(a);         // 输出: Lvalue referenceforwarder(10);        // 输出: Rvalue referencereturn 0;
}

3. 使用 std::movestd::forward

  • std::move:用于将左值强制转换为右值引用,从而触发移动语义。
  • std::forward:用于完美转发参数,保持参数的左右值性质。

总结

右值引用引入了 C++ 的资源转移能力,允许对象移动而非拷贝。使用右值引用能够显著提升程序的性能,并使 C++ 能够更好地支持现代应用中的高性能需求。

lambda

Lambda 表达式(匿名函数)是 C++11 引入的一种功能强大的语法,允许我们在不定义完整函数的情况下定义可调用对象。它通常用于需要临时函数的场合,例如在 STL 算法、线程、事件处理等场景中。下面是对 lambda 的基本概念、语法及其应用的详细介绍。

基本语法

Lambda 表达式的基本语法如下:

[capture](parameters) -> return_type {// 函数体
}
  • capture: 捕获外部变量的方式,可以是值捕获、引用捕获等。用 [] 括起来。
  • parameters: 输入参数,类似于普通函数。
  • return_type: 返回类型,可以省略,编译器会根据函数体推断。
  • function body: 函数体,可以包含任意的代码逻辑。

示例

以下是一些简单的示例,展示了 lambda 表达式的使用:

1. 简单的 Lambda 表达式
#include <iostream>int main() {auto add = [](int a, int b) { return a + b; };std::cout << "Sum: " << add(3, 5) << std::endl; // 输出: Sum: 8return 0;
}
2. 捕获外部变量
#include <iostream>int main() {int x = 10;auto printX = [&x]() { std::cout << "Value of x: " << x << std::endl; };printX(); // 输出: Value of x: 10x = 20;printX(); // 输出: Value of x: 20return 0;
}
3. 使用 Lambda 表达式作为 STL 算法参数
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 使用 lambda 表达式进行排序std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });for (int num : vec) {std::cout << num << " "; // 输出: 5 4 3 2 1}return 0;
}

捕获方式

  1. 值捕获: 使用 [=] 可以捕获外部作用域中的所有变量的值。

    int x = 10;
    auto f = [=]() { return x + 1; }; // x 被值捕获
    
  2. 引用捕获: 使用 [&] 可以捕获外部作用域中的所有变量的引用。

    int x = 10;
    auto f = [&]() { x++; }; // x 被引用捕获
    
  3. 混合捕获: 可以选择性地捕获某些变量。

    int x = 10, y = 20;
    auto f = [x, &y]() { return x + y; }; // x 为值捕获,y 为引用捕获
    

使用场景

  • 简化代码: Lambda 表达式使得代码更简洁,不需要单独定义一个函数。
  • 临时性操作: 当仅在特定上下文中使用的函数时,使用 lambda 更为合适。
  • 回调函数: 在需要异步处理或事件处理时,可以使用 lambda 表达式作为回调函数。

总结

Lambda 表达式为 C++ 提供了更灵活、更强大的功能,使得可以在需要时方便地定义和使用函数。它的引入使得许多编程模式变得更加简洁和易于维护。

获取Linux C/C++开发学习资料


http://www.mrgr.cn/news/64526.html

相关文章:

  • Observer 观察者模式
  • 「C/C++」C/C++标准库 之 #include<cstdlib> 通用工具函数库
  • 《现代工业经济和信息化》是什么级别的期刊?是正规期刊吗?能评职称吗?
  • 实体(Entity)详解
  • 春秋云境CVE-2022-21661,sqlmap+json一把梭哈
  • 数据结构-快速排序
  • 数字信号处理Python示例(3)生成三相正弦信号
  • 鸿蒙开发案例:分贝仪
  • Android中的Handle底层原理
  • 如何设置和使用低代码平台中的点击事件?
  • redis源码系列--(二)--eventlooop+set流程
  • 常用滤波算法(三)-算术平均滤波法
  • 【51蛋骗鸡单按键控制计数开始暂停复位】
  • 【ChatGPT】通过自定义参数让ChatGPT输出特定格式的文本
  • 同一局域网内A主机连接B主机的虚拟机中的服务
  • C++入门基础知识135—【关于C 库函数 - mktime()】
  • C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(1)
  • 非线性数据结构之树
  • 【Vue3】一文全览基础语法-案例程序及配图版
  • 【C++题解】1970. 判断是什么字符
  • DICOM标准:CT 模块及其在DICOM中的表示详解
  • 【星闪EBM-H63开发板】AT固件的接口简介
  • C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(2)
  • 文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《基于图注意力网络的配电网分布式光伏实时消纳能力评估方法 》
  • 高级 <HarmonyOS第一课>自由流转 的课后习题
  • ZFC in LEAN 之 前集(Pre-set)