Morse's Site
1186 字
6 分钟
通过 RIME 学习 C++ 系列:1
2024-06-03

C++ 的语法对我个人来说,可以说是目前所接触的编程语言中最复杂的,没有之一。

而为了编写针对仓输入法的功能模块,除了调用 librime 库的 api 外,还需要编写个性化的插件功能,所以 C++ 的学习也需要提上日程。

之前尝试过先从 C++ 的基础入门语法书去学习 C++,但效果不理想,学者前面遗忘着后面,所以我决定直接从 librime 的源码入手,边学边看。

namespace#

  • 命名空间定义:
namespace 空间名称1
{
    namespace 空间名称2
    {

    }
}
  • 命名空间的访问:
#include "命名空间头文件"

命名空间::实体名
  • 简略访问,即省略 ::
#include "命名空间头文件"

using namespace 空间名

// 命名空间::实体名可省略命名空间::
// 命名空间::实体名
实体名
注意

如 using namespace 多个空间,当空间出现重复实体名时,会编译错误。

using#

在前面的 namespace 中我们看到了 using 关键字,可以用来「引用命名空间中的名称」,从而可以省略 命名空间名称::

除了上面说得的用途外,using 还可以用来定义类型类型别名。

// plugins/predict/src/predict_db.h
using Candidates = ::rime::Array<::rime::table::Entry>;

这一行将为 ::rime::Array<::rime::table::Entry> 类型创建一个新名称 Candidates

::rime::Array<::rime::table::Entry>rime 命名空间提供的一种特定类型的数组。该数组存放类型为 ::rime::table::Entry 的对象。

通过创建这个别名,就可以在整个代码中使用 Candidates 代替 ::rime::Array<::rime::table::Entry>

这可以使代码更易于读写,尤其是在处理像这样的复杂类型时。

构造函数#

构造函数+初始化列表#

在调用构造函数的同时,可以初始化成员变量的值。

当成员变量为类类型时,使用此种写法效率会高一些,因为少调用了一次甚至多次成员变量相关的各种特殊成员函数,如构造函数等。

plugins/predict/src/predict_db.h

 public:
  PredictDb(const path& file_path)
      : MappedFile(file_path),
        key_trie_(new Darts::DoubleArray),
        value_trie_(new StringTable) {}

构造函数按以下方式初始化 PredictDb 对象:

  1. MappedFile(file_path):这是一个成员初始化器。它以 file_path 作为参数调用类 MappedFile 的构造函数。这表明 PredictDbMappedFile 的子类,而 file_path 用于初始化 PredictDb 对象的基础部分。
  2. key_trie_(new Darts::DoubleArray):这也是一个成员初始化函数。它用一个新的 Darts::DoubleArray 实例初始化成员变量 key_trie_new 关键字动态地为 Darts::DoubleArray 对象分配内存,并返回一个指向它的指针。
  3. value_trie_(new StringTable):同样,该函数用一个新的 StringTable 实例初始化成员变量 value_trie_
  4. {} 表示空的函数体

PredictDb 类的名称和初始化的成员来看,它似乎是一个预测数据库。它使用 Darts::DoubleArray 作为键(可能是为了高效查找),使用 StringTable 作为值。MappedFile 基础表明该数据库存储在一个文件中,并在构建 PredictDb 对象时提供了该文件的路径。

注意

类的 const 成员,只能使用初始化列表来初始化,而不能在构造函数内部进行赋值操作。

explicit#

使用关键字 explicit 修饰的构造函数,只能用于初始化和显式类型转换。

src/rime/dict/mapped_file.h

class RIME_API MappedFile {
 protected:
  explicit MappedFile(const path& file_path);

默认构造函数#

一个类中可以有多个构造函数,其中,没有参数的构造函数称为「默认构造函数」。

如果一个类中没有构造函数,那么编译器会自动生成一个默认构造函数。

= default#

C++11 中新增特性。

public:
    Time2() = default;

编译器可以为 = default 的函数自动生成函数体(等价于空函数 {}

= delete#

public:
    Time2() = delete;

告诉编译器,禁止生成 Time2() 默认构造函数。

析构函数#

类比 Swift。构造函数相当于 init() 函数,而析构函数则相当于 deinit {}

当对象被释放时,会调用。析构函数以~开头,后面跟类名

如:

public:
  ~Demo();

当使用抽象类时,析构函数一般为虚函数 virtual ~Demo()

类的成员函数#

在头文件(.h)的实现中(.cpp)使用 :: 编写具体的函数逻辑。

头文件:plugins/predict/src/predict_db.h

class PredictDb : public MappedFile {
 public:
  ...
  // 这是一个成员函数
  bool Load();
  ...

具体实现:plugins/predict/src/predict_db.cc

bool PredictDb::Load() {
    ...
}

函数模板#

声明语法:

template <typename T> // 定义函数模板
T functionName(T a, T b) {
    T add = a + b;
    return add;
}

如:src/rime/algo/strings.h

template <typename Iter, typename T>
string join(Iter start, Iter end, T&& delim) {
  string result;
  if (start != end) {
    result += (*start);
    start++;
  }
  for (; start != end; start++) {
    result += (delim);
    result += (*start);
  }
  return result;
}

类模板#

声明语法:

template <typename T>
// 或者
// template <class T>
class 类名
{

}

摘录书籍#

  • 《C++ 新经典》- 王健伟
通过 RIME 学习 C++ 系列:1
https://fuwari.vercel.app/posts/rime/rime_learn_01/
作者
Morse Hsiao
发布于
2024-06-03
许可协议
CC BY-NC-SA 4.0