levelDB的字符串Slice
上次我们分析编码解码的时候,注意到其中使用的参数有些是Slice
类型,这个类型就是levelDB中自己封装的一个轻量级字符串类,其只包含了指向字符串的指针和字符串的长度。
Slice可以理解成切片,因为其指向底层字符串的首地址,并且标定出长度。
但是需要注意的是,Slice中不涉及对字符串的销毁和创建,它只是指向 。因此调用Slice对象时必须确保底层的字符串没有被销毁 。
源码解析部分
没错,Slice对象只有一个头文件,全部方法都是内联的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 class LEVELDB_EXPORT Slice{ private : const char *data_; size_t size_; public : Slice () : data_ ("" ), size_ (0 ) {} Slice (const char *d, size_t n) : data_ (d), size_ (n) {} Slice (const std::string &s) : data_ (s.data ()), size_ (s.size ()) {} Slice (const char *s) : data_ (s), size_ (strlen (s)) {} Slice (const Slice &) = default ; Slice &operator =(const Slice &) = default ; const char *data () const { return data_; } size_t size () const { return size_; } bool empty () const { return size_ == 0 ; } char operator [](size_t n) const { assert (n < size ()); return data_[n]; } void clear () { data_ = "" ; size_ = 0 ; } void remove_prefix (size_t n) { assert (n <= size ()); data_ += n; size_ -= n; } std::string ToString () const { return std::string (data_, size_); } int compare (const Slice &b) const ; bool starts_with (const Slice &x) const { return ((size_ >= x.size_) && (memcmp (data_, x.data_, x.size_) == 0 )); } }; inline bool operator ==(const Slice &x, const Slice &y){ return ((x.size () == y.size ()) && (memcmp (x.data (), y.data (), x.size ()) == 0 )); } inline bool operator !=(const Slice &x, const Slice &y) { return !(x == y); }inline int Slice::compare (const Slice &b) const { const size_t min_len = (size_ < b.size_) ? size_ : b.size_; int r = memcmp (data_, b.data_, min_len); if (r == 0 ) { if (size_ < b.size_) r = -1 ; else if (size_ > b.size_) r = +1 ; } return r; }
Slice的实现还是非常简单的,而且它提供了非常丰富的接口。
但是Slice的实现我们也能看到一些问题:
由于Slice类不涉及内存的分配和销毁,也不检查内存是否合法,因此在调用的时候需要特别注意。
Slice类不涉及原子操作,虽然大部分都是const的读取,但是在多线程中将某一个Slice对象进行修改,则所有线程都需要执行同步。
关于第二点,源文件中也有说明
Multiple threads can invoke const methods on a Slice without external
synchronization, but if any of the threads may call a non-const method,
all threads accessing the same Slice must use external
synchronization.
多线程在调用const方法后可以不同步,但是如果调用非const方法,所有访问被修改的Slice的线程必须执行同步。
总结部分
编程小技巧
C++可以在类内或类外重载运算符,而在类内重载又可以分为'类内隐式重载'和在'类外显示重载',这几种方式区别如下。当然,此处不考虑友元。
1 2 3 4 5 6 7 8 9 10 11 12 class x { public : ... int operator ==(x& other){}; } int inline x::operator ==(x& other){};int inline operator ==(x& this , x& other){};