Morse's Site
1845 字
9 分钟
通过 RIME 学习 C++ 系列:2
2024-06-04

lambda 表达式#

plugins/predict/src/predictor.cc

  select_connection_ = context->select_notifier().connect(
      [this](Context* ctx) { OnSelect(ctx); });

lambda 是 C++ 11 中的特性,它是一种匿名函数。

语法:

[捕获列表](参数列表) -> 返回值 { 函数体; };
  • 编译器可简单的推导出 返回值 的类型,当可推导时,可省略 -> 返回值 的声明。
  • 当函数没有参数列表时,参数列表可省略,即 () 可省略。
  • 捕获列表 [] 与函数体 {} 任何情况下都必须存在。
  • 函数体后分号,即 {};,不能省略

捕获列表#

  • []: 表示不捕获任何变量
  • [&]: 表示捕获外部作用域中全部变量,并作为引用在函数体内使用。 注意:程序必须保证,调用时,引用的变量没有超过其作用域。
  • [=]: 表示捕获外部作用域中全部变量,并作为在函数体内使用。 表示:只能读取,不能赋值。
  • [this]: 用于类中,表示捕获当前类的 this 指针,目的是让 lambda 函数具有当前类成员函数相同的访问权限。 注意:
    • 如果使用了[&] [=],那么默认就相当于捕获了 this
    • [=] 或者 [this],只可读取当前类的成员变量,不可修改,如需修改,请使用 [&]
  • 变量的值捕获[变量名]与引用捕获[&变量名]
    • [变量名]: 值捕获,不能修改变量的值。
    • [&变量名]: 引用捕获,可修改变量的值。
  • 组合捕获
    • [=, &变量名]: 按值捕获外部全部变量,但按引用捕获后面的变量。
    • [&, 变量名]: 按引用捕获外部全部变量,但按值捕获后面的变量。

lambda 中的 mutable#

语法:

[捕获列表](参数列表) mutable -> 返回值 { 函数体; };

注意:有了 mutable 修饰后,参数列表的括号是不能省略。

使用 mutable 修饰后,函数体内可修改外部作用域下的常量属性的值。

smart pointer 智能指针#

智能指针是为了防止内存泄漏,而将内存的释放工作交给智能指针处理。

当不使用智能指针时,需要使用 newdelete 申请和释放内存,且它俩是必须成对使用。

mallocfree 是 C 语言中的内存申请与释放的关键字。newdelete 是 C++ 中的内存管理关键字。

智能指针是对 new 关键字返回的指针的包装,它可以自动释放申请的内存。

C++ 标准库中有 4 种智能指针:std::auto_ptrstd::unique_ptrstd::shard_ptrstd::weak_ptr

注意:std::auto_ptr 是 C++ 98 版本中的智能指针,在 C++ 11 中已经完全被 std::unique_ptr 替代,所以不要在使用 std::auto_ptr

unique_ptr:是独占式指针,同一时间有且只有一个指针指向该对象,当然,对象的所有权是可以移交出去的。 shared_ptr:是共享式指针,多个指针可以指向同一个对象,当最后一个指针被销毁时,则指向的对象也会被释放。 weak_ptr: 是用来辅助 shared_ptr 指针。

shared_ptr#

shared_ptr 工作机制是 引用计数。当指针指向的对象需要被共享时,应该使用 shared_ptr

语法:

std::shared_ptr<指向类型> 指针名;

如:

// 形式1:
std::shared_ptr<int> p1(new int(100));

// 错误的形式
// std::shared_ptr<int> p2 = new int(100);

// 形式2: 使用标准库中的 make_shared 函数构建
std::shared_ptr<int> p3 = std::make_shared<int>(100);

注意:使用 std::make_shared 函数构建的 shared_ptr 指针不能自定义删除器

  • use_count() 返回指针引用计数的数量。用于调试,效率不高。
  • unique() 返回 bool 值,表示是否有指针指向该对象。
  • reset()
    • 不带参数时, 即 p1.reset(),对象的应用计数减1,同时当前指针指向 nullptr
    • new参数,如 p1.reset(new int(200)),释放指针原指向的对象,同时指针指向新的对象。
    • 空指针使用 reset(new xx) 初始化,如 std::shared_ptr<int> p; p.reset(new int(100))
  • *解引用:*指针解引用指针指向的对象
  • get() 返回智能指针中保存的指针。
  • std::swap(p1, p2): 交换两个指针指向的对象
  • = nullptr 将指针置为空
  • if (智能指针):判断指针是否指向一个对象。

删除器#

语法:std::shared<int> p1(new int(100), 指定删除器)

默认删除器不支持删除一些如数组对象,这时就需要指定默认删除器。

std::shared_ptr<int[]> p(new int[10], [](int *p){
    delete[] p;
});
p.reset();

可以将 default_delete<T>() 类模板作为默认删除器。

std::shared_ptr<A> p(new A[10], std::default_delete<A[]>());
p.reset();

weak_ptr#

与 Swift 中的 weak 含义相同,指不改变对象的引用计数,则控制不了对象的生命周期。

std::weak_ptr<int> piw(pi)
  • use_count() 返回对象的引用计数数量。
  • expired() 弱引用的对象是否存在,当引用计数的数量为 0 时,返回 false。
  • reset() 将指针至为 nullptr
  • lock(): 返回一个shared_ptr,同时指向对象的引用计数加1
auto p1 = make_shared<int>(100);
weak_ptr<int> pw(p1);
if (!pw.expired()) {
    auto p2 = pw.lock(); // p1 引用计数加 1
    if (p2 != nullptr){
        // 通过 p2 访问对象
    }
    // 离开此范围,p1 引用计数减1
}

unique_ptr#

同一时间只有一个 unique_ptr 指针指向这个对象,且当指针销毁时,对象也会被释放。

// 方式 1
std::unique_ptr<int> p(new int(100));

// 方式 2
auto p2 = std::make_unique<int>(100)
注意
  1. std::make_unique() 函数是 C++ 14 提供。
  2. std::make_unique() 不能使用删除器。

unique_ptr 的操作#

  1. 不支持复制操作。
std::unique_ptr<string> p1(new string("Hello,World!"));
// 以下是错误的
std::unique_ptr<string> p2(p1);
std::unique_ptr<string> p3 = p1;
  1. std::move() 将一个 unique_ptr 指针转移到其他 unique_ptr,转移后原指针为 nullptr
std::unique_ptr<string> p1(new string("Hello,World!"));
std::unique_ptr<string> p2 = std::move(p1); // 将 p1 指向的对象转移至 p2,同时 p1 指向 nullptr
  1. release() 成员函数:释放对裸指针的控制权,并返回裸指针。返回的裸指针可以使用 delete 控制,也可以用来初始化另一个智能指针。
std::unique_ptr<string> p1(new string("Hello,World!"));
std::unique_ptr<string> p2(p1.release());

// 或者
std::unique_ptr<string> p1(new string("Hello,World!"));
string* str = p1.release()
delete str;
  1. reset() 成员函数

    • 不代参数,释放指针指向的对象,同时指针指向 nullptr
    • 代参数,释放指针指向的对象,同时指针指向新的参数。
  2. = nullptr 释放指针指向的对象,同时指针指向 nullptr

  3. get() 成员函数:获取指针指向对象的裸指针。

  4. *解引用,获取指针指向的对象。

  5. std::swap() 交换两个指针指向的对象

  6. 转为 shared_ptr 指针:当 unique_ptr 为右值时,可以赋值给 shared_ptr,此时 shared_ptr接管指针指向的对象。

unique_ptr 的删除器#

unique_ptr<指向对象类型,删除器> 指针名
typedef void(* fp)(string*); // 函数类型指针
void myDelete(string *pdel) {
    delete pdel;
    pdel = nullptr;
}
std::unique_ptr<string, fp> p1(new string("Hello,World!"), myDelete);

auto mydella = [](string *pdel) {
    delete pdel;
    pdel = nullptr;
};
std::unique_ptr<string, decltype(mydella)> p2(new string("Hello,World!"), mydella);

RIME 中的示例#

在 RIME 的 src/rime/common.h 中,如下:

template <class T>
using the = std::unique_ptr<T>;

template <class T>
using an = std::shared_ptr<T>;

template <class T>
using weak = std::weak_ptr<T>;

template <class T, class... Args>
inline an<T> New(Args&&... args) {
  return std::make_shared<T>(std::forward<Args>(args)...);
}

在 RIME 中 分别使用类型别名:the 表示 unique_ptr, an 表示 shared_ptr, weak 表示 weak_ptr 三种智能指针。

使用 New 模板函数构建一个新的 shared_ptr 指针。

std::forward<Args>(args)... 用来将参数转发给 T 的构造函数。

template <class X, class Y>
inline an<X> As(const an<Y>& ptr) {
  return std::dynamic_pointer_cast<X>(ptr);
}

template <class X, class Y>
inline bool Is(const an<Y>& ptr) {
  return bool(As<X, Y>(ptr));
}

As 用来做 shared_ptr 指针类型转换。其中std::dynamic_pointer_cast<X>(ptr) 被调用来执行动态转换。 std::dynamic_pointer_cast 是一个对 shared_ptr 执行动态转换的函数。如果动态转换成功,则返回一个新类型的 shared_ptr;如果不成功,则返回 nullptr。在本例中,它尝试将 shared_ptr 类型的 ptr 转换为 X 类型的 shared_ptr

Is 用来判断 shared_ptr 类型 ptr 是否为类型 X。

通过 RIME 学习 C++ 系列:2
https://fuwari.vercel.app/posts/rime/rime_learn_02/
作者
Morse Hsiao
发布于
2024-06-04
许可协议
CC BY-NC-SA 4.0