链式调用
认为链式调用是一种代码风格,在一些情况下能够使代码容易理解。比如:
Student studentFromChina;
studentFromChina.setName("Alice");
studentFromChina.setAge(14);
studentFromChina.setGrade('A');
// 链式
studentFromChina.setName("Alice")
.setAge(14)
.setGrade('A')
可以看到,如果变量比较复杂,通过链式的风格,能够帮助我们把注意力集中到关键的地方。
单个类的情况
如果只涉及单个类,使用C++来实现支持链式调用的类还是非常简单的。如
class BytesReader {
private:
char* data;
int cursor;
public:
BytesReader(char *data): data(data), cursor(0) {};
BytesReader* move(int step) {
this->cursor += step;
return this;
}
BytesReader* reset();
BytesReader* read_int(int &val, int num_of_bytes);
...
};
BytesReader
能够提供在一个 char
数组上随机如取 int范围的值的功能。每次函数调用完会将自身返回就可以用于链式调用,写出链式调用的风格,如:
reader->read_int(a, 4)->read_int(b, 4)->read_int(c, 4);
多个类的情况
假设还有一个类如下:
class BytesWriter {
private:
char* data;
int cursor;
public:
BytesWriter(char *data): data(data), cursor(0) {};
BytesWriter* move(int step) {
this->cursor += step;
return this;
}
BytesWriter* reset();
BytesWriter* write_int(int &val, int num_of_byte);
...
};
对于这个类,理所当然想把他们公用的部分抽取出来,如move, reset
。
所以,我们可以试着写出如下类来:
class BytesHandler {
protected:
char* data;
int cursor;
public:
BytesHandler(char* data): data(data), cursor(0) {}
BytesHandler* move();
BytesHandler* reset();
};
如果把这个类当成基类,那么就可以通过继承的方式派生出 BytesReader, BytesWriter
来了
class BytesReader: public BytesHandler {...};
class BytesWriter: public BytesHandler {...};
但是,这样子真的可以吗?对于如下的调用,真的会达到预期的效果吗?
reader->move(8)->read_int(a, 4)->read_int(b, 4);
对于 move
这个函数来说,可以发现它的返回值时 BytesHandler*
,然而 BytesHandler
是没有 read_int
的方法的,所以上述代码显然会报错。
如果想要把 move
的方法放到父类里面,且返回值是派生类,暂时只想到了用模板的方法类做,也就是说,move
函数的返回值是 T *
,当BytesReader
继承父类时,必须制定这个T = BytesReader
,也就是它本身,这样就可以达到我们的目的了。
template<class T>
class BytesHandler {
private:
char *data;
char cursor;
public:
BytesHandler(char *data):data(data), cursor(0) {};
T* move(int steps) {
this->cursor += steps;
return (T*) this; // 后面找资料的时候,发现一般用 static_cast<T*>(this)来做
}
T* reset();
};
class BytesReader: public BytesHandler<BytesReader> {
public:
BytesReader(char *data): BytesHandler<BytesReader>(data) {};
BytesReader* read_int(int &val, int num_of_bits);
};
class BytesWriter: public BytesHandler<BytesWriter> {
public:
BytesWriter(char *data): BytesHandler<BytesWriter>(data) {};
BytesWriter* read_int(int &val, int num_of_bits);
};
多继承
如果想要一个类既有BytesReader
和BytesWriter
的功能,那么是否可以直接用一个类继承 BytesReader
和 BytesWriter
呢?
class BytesReadWriter: public BytesReader,
public BytesWriter {...};
然而,这样是不行的,这样会导致几个问题:
-
第一个是
BytesWriter,BytesReader
都有一份BytesHandler
的副本,一般来说,多继承用虚继承来防止出现这个问题。那么,BytesWriter 和 BytesReader 与 BytesHandler之间必须也是虚继承的关系。然而,如果一旦把继承关系改成虚拟继承,T* move(int steps) { this->cursor += steps; return (T*) this; }
就会报错,
error: cannot convert from pointer to base class ‘BytesHandler<BytesWriter>’ to pointer to derived class ‘BytesWriter’ because the base is virtual
(原因待查明)有一个小的技巧可以解决这个问题。
template<class T> class BytesHandler { protected: Bytes* data; int cursor; public: BytesHandler() {}; BytesHandler(Bytes* data): data(data), cursor(0) {}; T* move(int steps) { this->cursor += steps; return this->self(); } T* reset(); virtual T* self() = 0; };
在派生来中,实现虚函数
self
来返回自身的指针,作为虚函数,它是动态绑定的,因此是在运行的时候来决定运行具体代码的片段的,就可以绕过这个错误。也可以用
dynamic_cast<T*>
来替换(T*)
。最后继承的代码改成:
class BytesReader: virtual public BytesHandler<BytesReader> { public: BytesReader() {}; BytesReader(Bytes *data): BytesHandler<BytesReader>(data) {}; BytesReader* read_int(int& val, int num); BytesReader* read_bytes(Bytes *&bytes, int num); BytesReader* self(); }; class BytesWriter: virtual public BytesHandler<BytesWriter> { public: BytesWriter() {}; BytesWriter(Bytes* data): BytesHandler<BytesWriter>(data) {}; BytesWriter* write_int(int val, int num); BytesWriter* write_bytes(Bytes *bytes, int num); BytesWriter* self(); }; class BytesReaderWriter: public BytesReader, public BytesWriter, public BytesHandler<BytesReaderWriter> { public: BytesReaderWriter() {}; BytesReaderWriter(Bytes *data): BytesHandler<BytesReaderWriter>(data) {}; BytesReaderWriter* self(); };
然而,改成这样,代码还是无法运行的。原因在于还是有多份副本。为什么用虚继承,还是没有解决多份副本的问题?
原因应该是,
BytesHandler<BytesReader>
和BytesHandler<BytesReader>
虽然都是BytesHandler
,但是实际上它们是不同的类,因此可以再做一个抽象类出来;class BaseBytesHandler { protected: char* data; int cursor; public: BaseBytesHandler() {}; BaseBytesHandler(char* data): data(data), cursor(0) {}; virtual ~BaseBytesHandler() {}; }; template<class T> class BytesHandler: virtual public BaseBytesHandler { public: BytesHandler() {}; BytesHandler(char* data): BaseBytesHandler(data) {}; T* move(int num); T* reset(); virtual T* self() = 0; }; class BytesReader: public BytesHandler<BytesReader> { public: BytesReader() {}; BytesReader(char *data): BaseBytesHandler(data) {}; BytesReader* read_int(int& val, int num); BytesReader* read_bytes(Bytes *&bytes, int num); BytesReader* self(); }; class BytesWriter: public BytesHandler<BytesWriter> { public: BytesWriter() {}; BytesWriter(char* data): BaseBytesHandler(data) {}; BytesWriter* write_int(int val, int num); BytesWriter* write_bytes(Bytes *bytes, int num); BytesWriter* self(); }; class BytesReaderWriter: virtual public BytesReader, virtual public BytesWriter, virtual public BytesHandler<BytesReaderWriter> { public: BytesReaderWriter() {}; BytesReaderWriter(char *data): BaseBytesHandler(data) {}; BytesReaderWriter* self(); };
这样,虚继承公用的基类就是
BaseBytesHandler
了,就可以解决上述多份副本的问题了。 -
第二个问题,
BytesReadWriter
继承自BytesReader
和BytesWriter
,而它们都有move
和reset
函数,所以BytesReadWriter
需要显式的申明调用那个。readwriter->BytesHandler<BytesReaderWriter>::reset();
-
第三个问题,
BytesReadWriter
的实例,调用read_int
函数后返回的是BytesReader
的指针,就不能调用write_int
的方法了。
针对这些问题,有一个解决方案:使用CRTP的方式,来组织这些类。(CRTP是什么,可以参考后文的链接)如:
template<class T>
class Handlable {
public:
T* move(int steps) {
self()->cursor += steps;
return self();
}
T* reset();
T* self() {
return static_cast<T*>(this);
}
private:
Handlable() {};
friend T;
};
template<class T>
class Writerable {
public:
T* write_int(int val, int num_of_bytes);
T* write_bytes(Bytes *bytes, int num_of_bytes);
T* self();
private:
Writerable() {};
friend T;
};
通过
class BytesWriter: public Handlable<BytesWriter>, public Writerable<BytesWriter> {...};
这样的形式,从而可以解决上述的三个问题。对于第一个问题,上述没有棱形继承,所以也不会有多份副本存在导致二义性的问题,如果把self
再单独拿出来,做一层继承,也可以用虚继承的方式来解决多份副本的问题(如果用虚继承,self函数中的static_cast 需要用dynamic_cast 来替代)。对于第二个问题,每次函数调用完后可以返回self函数的返回值,而这个函数返回的是最终模板T的指针,而这个T是唯一的,也就是类本身,所以不会有二义性问题。对于第三个问题,也是因为返回的T的指针,可以解决。
涉及的解决方案
参考博客
CRTP
mixin & crtp
CRTP 代码整理
common.h
# ifndef __DATA_TYPE_H__
# define __DATA_TYPE_H__
typedef unsigned char byte;
const int BLOCK_SIZE = 4096;
const int BLOCK_HEADER_SIZE = 4 * sizeof(int);
const int BYTE_ARRAY_INITIAL_SIZE = 512;
const int STRING_INTIAL_SIZE = 512;
class Bytes {
private:
byte* data;
int capacity;
int length;
public:
Bytes();
Bytes(byte* buffer, int num);
Bytes(byte fill, int num);
~Bytes();
byte &operator[] (int index);
const byte &operator[] (int index) const;
bool operator== (const Bytes &other) const;
bool operator!= (const Bytes &other) const;
Bytes* slice(int);
int len() const;
};
# endif
handler.h
# ifndef __READER_H__
# define __READER_H__
# include "common.h"
# include "assert.h"
template<class T>
class Functionality {
public:
virtual T* self() {return dynamic_cast<T*>(this);}
};
template<class T>
class Handlable: virtual public Functionality<T> {
public:
T* move(int steps);
T* reset();
private:
Handlable() {};
friend T;
};
template<class T>
class Readable: virtual public Functionality<T> {
public:
T* read_int(int& val, int num_of_bytes);
T* read_bytes(Bytes *&bytes, int num_of_bytes);
private:
Readable() {};
friend T;
};
template<class T>
class Writerable: virtual public Functionality<T> {
public:
T* write_int(int val, int num_of_bytes);
T* write_bytes(Bytes *bytes, int num_of_bytes);
private:
Writerable() {};
friend T;
};
class BaseBytesHandler {
public:
Bytes* data;
int cursor;
public:
BaseBytesHandler(Bytes* data): data(data), cursor(0) {};
virtual ~BaseBytesHandler() {};
};
class BytesReader: public BaseBytesHandler,
public Handlable<BytesReader>,
public Readable<BytesReader> {
public:
BytesReader(Bytes *data): BaseBytesHandler(data) {};
};
class BytesWriter: public BaseBytesHandler,
public Handlable<BytesWriter>,
public Writerable<BytesWriter> {
public:
BytesWriter(Bytes* data): BaseBytesHandler(data) {};
};
class BytesReaderWriter: public BaseBytesHandler,
public Handlable<BytesReaderWriter>,
public Readable<BytesReaderWriter>,
public Writerable<BytesReaderWriter> {
public:
BytesReaderWriter(Bytes* data): BaseBytesHandler(data) {};
};
# endif
handler.cpp
# include "handler.h"
# include "assert.h"
template<class T>
T* Handlable<T>::move(int steps) {
assert(0 <= (this->self())->cursor + steps && (this->self())->cursor + steps <= (this->self())->data->len());
(this->self())->cursor += steps;
return (this->self());
}
template<class T>
T* Handlable<T>::reset() {
(this->self())->cursor = 0;
return (this->self());
}
template<class T>
T* Readable<T>::read_int(int &val, int num) {
assert(0 < num && num <= 4);
val = 0;
for (int i = 0, base = 1; i < num; i++, base *= 256) {
val += base * (int)((*(this->self())->data)[(this->self())->cursor]);
(this->self())->cursor ++;
}
return (this->self());
}
template<class T>
T* Readable<T>::read_bytes(Bytes *&byte_array, int num) {
assert((this->self())->cursor + num <= (this->self())->data->len());
byte* bytes = new byte[num];
for (int i = 0; i < num; i++) {
bytes[i] = (*(this->self())->data)[(this->self())->cursor];
(this->self())->cursor ++;
}
byte_array = new Bytes(bytes, num);
delete[] bytes;
return (this->self());
}
template<class T>
T* Writerable<T>::write_int(int val, int num) {
assert((this->self())->cursor + num <= (this->self())->data->len());
for (int i = 0, tmp = val; i < num; i++, tmp /= 256) {
(*(this->self())->data)[(this->self())->cursor] = tmp % 256;
(this->self())->cursor ++;
}
return (this->self());
}
template<class T>
T* Writerable<T>::write_bytes(Bytes* bytes, int num) {
assert((this->self())->cursor + num <= (this->self())->data->len());
assert(num <= bytes->len());
for (int i = 0; i < num; i++) {
(*(this->self())->data)[(this->self())->cursor] = (*bytes)[i];
(this->self())->cursor ++;
}
return (this->self());
}
template class Readable<BytesReader>;
template class Readable<BytesReaderWriter>;
template class Writerable<BytesWriter>;
template class Writerable<BytesReaderWriter>;
template class Handlable<BytesReader>;
template class Handlable<BytesWriter>;
template class Handlable<BytesReaderWriter>;
网友评论