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 对象:
MappedFile(file_path)
:这是一个成员初始化器。它以file_path
作为参数调用类MappedFile
的构造函数。这表明PredictDb
是MappedFile
的子类,而file_path
用于初始化PredictDb
对象的基础部分。key_trie_(new Darts::DoubleArray)
:这也是一个成员初始化函数。它用一个新的Darts::DoubleArray
实例初始化成员变量key_trie_
。new
关键字动态地为Darts::DoubleArray
对象分配内存,并返回一个指向它的指针。value_trie_(new StringTable)
:同样,该函数用一个新的StringTable
实例初始化成员变量value_trie_
。{}
表示空的函数体
从 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++ 新经典》- 王健伟