【C++14保姆级教程】lambda 初始化捕获 new/delete 消除

简介: 【C++14保姆级教程】lambda 初始化捕获 new/delete 消除

前言


Lambda表达式是C++11引入的一种便捷的语法结构,可以用于创建匿名函数。C++14进一步扩展了Lambda的功能,其中包括初始化捕获(init capture)的特性。在本文中,我们将介绍Lambda初始化捕获的概念、使用方法以及几个示例代码,帮助您更好地理解和应用它。

在C++中,使用原始指针来管理动态分配的内存可能导致内存泄漏和悬挂指针等严重问题。为了解决这些问题,C++11引入了智能指针的概念,其中包括unique_ptr、shared_ptr和weak_ptr。这些智能指针提供了自动化的内存管理和所有权追踪,使得内存管理更加安全和方便。本文将介绍这三种智能指针的概念、使用方法、功能以及成员函数,并提供几个示例代码,以帮助您理解和应用智能指针。


一、Lambda表达式捕获列表


1.1 Lambda表达式捕获列表是什么?

在Lambda表达式中,通过捕获列表(capture list)可以捕获外部变量,以便在Lambda函数体内使用。C++14引入了初始化捕获的特性,它允许我们在捕获列表中使用等号(=)或者引用符号(&)来初始化捕获的变量。初始化捕获可以捕获特定的值或者引用,并在Lambda函数体内持久化使用。


1.2 Lambda表达式捕获列表使用

使用初始化捕获时,可以使用等号(=)来捕获外部变量的副本,或者使用引用符号(&)来捕获外部变量的引用。下面是一些使用伪代码的示例,以帮助您理解Lambda初始化捕获的用法:


1.3 值捕获的示例

int x = 10;
auto lambda = [value = x]() {
    // 在Lambda函数体中使用被捕获的值
    std::cout << "Value: " << value << std::endl;
};
lambda(); // 输出:Value: 10


在上述示例中,我们通过值捕获方式将变量x的值捕获到Lambda函数内部,并将其存储在名为value的变量中。Lambda函数体内可以使用value变量,即使x的值发生了变化,value仍然保持捕获时的初始值。


1.4 引用捕获的示例

int y = 5;
auto lambda = [&ref = y]() {
    // 在Lambda函数体中使用被捕获的引用
    std::cout << "Reference: " << ref << std::endl;
};
lambda(); // 输出:Reference: 5
y = 20;
lambda(); // 输出:Reference: 20


在上述示例中,我们通过引用捕获方式将变量y的引用捕获到Lambda函数内部,并将其存储在名为ref的变量中。Lambda函数体内使用的是y的引用,因此当y的值发生变化时,Lambda函数中的ref也会随之改变。


1.5 初始化捕获与可变性(mutable)

int z = 7;
auto lambda = [value = z]() mutable {
    // 在Lambda函数体中修改被捕获的值
    value += 3;
    std::cout << "Modified Value: " << value << std::endl;
};
lambda(); // 输出:Modified Value: 10
lambda(); // 输出:Modified Value: 10 (初始值没有改变)


在上述示例中,我们使用初始化捕获方式将变量z的值捕获到Lambda函数内部,并将其存储在名为value的变量中。由于使用了mutable关键字修饰Lambda函数,在函数体内部可以修改被捕获的值,而不影响外部变量。


1.6 示例代码

下面是使用Lambda初始化捕获的综合示例代码:

#include <iostream>
int main() {
    int x = 42;
    auto lambda1 = [value = x]() {
        std::cout << "Value: " << value << std::endl;
    };
    lambda1(); // 输出:Value: 42
    auto lambda2 = [&ref = x]() {
        std::cout << "Reference: " << ref << std::endl;
    };
    lambda2(); // 输出:Reference: 42
    auto lambda3 = [value = x]() mutable {
        value += 10;
        std::cout << "Modified Value: " << value << std::endl;
    };
    lambda3(); // 输出:Modified Value: 52
    lambda3(); // 输出:Modified Value: 52 (初始值没有改变)
    return 0;
}


二、智能指针的概念


智能指针是一种封装了动态分配的内存的对象,它负责在适当的时机销毁自身所管理的内存。以下是每个智能指针的概念:


2.1 unique_ptr

unique_ptr是独占所有权的智能指针,它确保只有一个指针可以访问并管理分配的内存。当unique_ptr超出作用域或被显式释放时,它所管理的内存会被自动释放。


2.2 shared_ptr

shared_ptr是共享所有权的智能指针,它可以被多个指针共享,并且会跟踪对象的引用计数。只有当最后一个shared_ptr超出作用域或显式释放时,所管理的内存才会被释放。


2.3 weak_ptr

weak_ptr是一种弱引用的智能指针,它允许您观察shared_ptr所管理的对象,但不会增加引用计数。当最后一个shared_ptr释放后,weak_ptr自动失效。


2.4 使用:

下面是使用伪代码展示如何使用这三种智能指针:

使用unique_ptr:
{
    unique_ptr<MyClass> ptr(new MyClass()); // 创建unique_ptr并分配内存
    ptr->SomeFunction(); // 使用unique_ptr访问对象的成员函数
} // unique_ptr超出作用域,自动释放分配的内存


使用shared_ptr:
{
    shared_ptr<MyClass> ptr1(new MyClass()); // 创建shared_ptr并分配内存
    shared_ptr<MyClass> ptr2 = ptr1; // 多个shared_ptr共享同一块内存
    ptr1->SomeFunction(); // 使用shared_ptr访问对象的成员函数
} // 最后一个shared_ptr超出作用域,自动释放内存
使用weak_ptr:
{
    shared_ptr<MyClass> sharedPtr(new MyClass()); // 创建shared_ptr并分配内存
    weak_ptr<MyClass> weakPtr = sharedPtr; // 创建weak_ptr观察所管理的对象
    if (sharedPtr) { // 检查shared_ptr是否有效
        sharedPtr->SomeFunction(); // 使用shared_ptr访问对象的成员函数
    }
} // shared_ptr超出作用域,weak_ptr自动失效

2.5 成员函数

每种智能指针都提供了一组成员函数来管理内存和访问对象。以下是常用的成员函数:

1. unique_ptr:

  • get():返回原始指针
  • reset():释放当前指针,并接管新的指针
  • release():释放当前指针的所有权,返回原始指针

2. shared_ptr:

  • get():返回原始指针
  • reset():释放当前指针,并接管新的指针
  • use_count():返回当前对象的引用计数

3. weak_ptr:

  • expired():检查weak_ptr所观察的shared_ptr是否过期
  • lock():将weak_ptr提升为shared_ptr


2.5 示例代码

下面是三个使用智能指针的示例代码:

#include <iostream>
#include <memory>
class MyClass {
public:
    void SomeFunction() {
        std::cout << "Hello from MyClass!" << std::endl;
    }
};
int main() {
    // 示例1:使用unique_ptr
    {
        std::unique_ptr<MyClass> ptr(new MyClass());
        ptr->SomeFunction();
    }
    // 示例2:使用shared_ptr
    {
        std::shared_ptr<MyClass> ptr1(new MyClass());
        std::shared_ptr<MyClass> ptr2 = ptr1;
        ptr1->SomeFunction();
    }
    // 示例3:使用weak_ptr
    {
        std::shared_ptr<MyClass> sharedPtr(new MyClass());
        std::weak_ptr<MyClass> weakPtr = sharedPtr;
        if (auto shared = weakPtr.lock()) {
            shared->SomeFunction();
        }
    }
    return 0;
}



总结


Lambda初始化捕获是C++14中有用的特性之一,它进一步增强了Lambda的灵活性和功能。通过初始化捕获,我们可以捕获外部变量的特定值或引用,并在Lambda函数体内持久化使用。我们可以使用等号(=)来进行值捕获,使用引用符号(&)进行引用捕获,并且还可以结合mutable关键字修改捕获的值。掌握Lambda初始化捕获的用法,可以让我们更好地编写清晰、简洁且功能强大的代码。

通过使用unique_ptr、shared_ptr和weak_ptr,我们可以更安全地管理动态分配的内存,避免了内存泄漏和悬挂指针等错误。unique_ptr提供独占所有权,shared_ptr允许多个指针共享所有权,并提供引用计数,而weak_ptr允许观察shared_ptr所管理的对象而不增加引用计数。了解这些智能指针的概念、使用方法和成员函数,能够帮助我们编写更可靠、易于维护的C++代码,并提高程序的性能和安全性。

相关文章
|
26天前
|
C++
C++ 异常机制问题之捕获异常的问题如何解决
C++ 异常机制问题之捕获异常的问题如何解决
|
4天前
|
Dart API 开发工具
Dart ffi 使用问题之Dart API要在C++中使用,该如何初始化
Dart ffi 使用问题之Dart API要在C++中使用,该如何初始化
|
1月前
|
安全 编译器 C++
C++一分钟之-泛型Lambda表达式
【7月更文挑战第16天】C++14引入泛型lambda,允许lambda接受任意类型参数,如`[](auto a, auto b) { return a + b; }`。但这也带来类型推导失败、隐式转换和模板参数推导等问题。要避免这些问题,可以明确类型约束、限制隐式转换或显式指定模板参数。示例中,`safeAdd` lambda使用`static_assert`确保只对算术类型执行,展示了一种安全使用泛型lambda的方法。
26 1
|
1月前
|
存储 C语言 C++
【C/C++】动态内存管理( C++:new,delete)
C++的`new`和`delete`用于动态内存管理,分配和释放内存。`new`分配内存并调用构造函数,`delete`释放内存并调用析构函数。`new[]`和`delete[]`分别用于数组分配和释放。不正确匹配可能导致内存泄漏。内置类型分配时不初始化,自定义类型则调用构造/析构。`operator new`和`operator delete`是系统底层的内存管理函数,封装了`malloc`和`free`。定位`new`允许在已分配内存上构造对象,常用于内存池。智能指针等现代C++特性能进一步帮助管理内存。
|
2月前
|
C++
C/C++内存管理(2):`new`和`delete`的实现原理
C/C++内存管理(2):`new`和`delete`的实现原理
|
1月前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
1月前
|
编译器 C++
【C++】详解初始化列表,隐式类型转化,类静态成员,友元
【C++】详解初始化列表,隐式类型转化,类静态成员,友元
|
2月前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。
|
2月前
|
容器
C++11 列表初始化(initializer_list),pair
C++11 列表初始化(initializer_list),pair
|
6天前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
13 0