一直以来都是通过C用基于对象的设计方法来写代码。即使工作中使用C++, 也是尽可能少的使用超出C的一些特性。当然这并不是C++不好,而是C++实在太复杂了。以我的脑力来讲, 如果使用C++过多的特性, 很容易让我过于陷入语法特性之中, 而忽略了设计。因此, 对于C++的一些高级特性, 如模板等并没有深入研究过。
模板对我来讲, 仅限于知道可以实现泛型。至于怎么巧妙的利用泛型来实现其他特性, 从来没有深入研究过。最近工作中,碰到了一些看起来比较高端的模板用法,令人有一种耳目一新的感觉,因此就记录一下。
如果实现一个单纯的组播模块, 该模块提供的组播接口可能类似:
void multicast(func, arg1, arg2, arg3, ...);
在不用模板的情况下,假设每种参数有可能有m种类型,如果参数的个数为n个。 那么针对参数为n个的函数就需要手写m^n个重载板本。
使用模板推导功能,对于参数个数为n个的函数,就仅仅只需要实现一个实现即可。
示意代码大概如下:
template
int call(void (*func)(T1 p1, T2 p2), T1 p1, T2 p2)
{
        func(p1, p2);
}
void func1(int a, float b)
{
        printf("%d, %f\n", a, b);
}
void func2(double a, const char *b)
{
        printf("%lf, %s\n", a, b);
}
int main()
{
        call(func1, 3, 3.5f);
        call(func2, 5.3, "hello");
        return 0;
}
这样使用模板忽略掉了参数类型,仅仅针对参数个数进行重载, 会大大提高代码的编写效率。
对于C++来讲,其实RAII应该算是惯用手段了。
在不使用模板的情况下,如果我们使用RAII来管理指针,我们就需要为每一个类实现一个指针类。而如果使用模板,仅仅实现一个指针类就可以了。
比如:
template
class PTR {
public:
        PTR(T a) {
                ptr = a;
        };
        ~PTR() {
                if (ptr == NULL)
                        return;
                delete ptr;
        };
        T get() {
                return ptr;
        };
private:
        T ptr;
};
class test {
public:
        test() {printf("test\n");}
        ~test() {printf("~test\n");}
        void hello() {printf("%s\n", h);}
        void set(const char *n) {h = n;}
private:
        const char *h;
};
int main()
{
        PTR
        test *t = p.get();
        t->set("hello");
        t->hello();
        return 0;
}
其实上面的代码大致就是C++11提供的std::unique_ptr实现的功能了.
在网络通信过程中,定义数据结构总是一件很烦的事,使用纯C的数据结构便于传输,但是会将结构定义的很死, 而且数据种类有限。
如果使用vector/unordered_map并且相互嵌套时传输就会很麻烦。一般这时候就不得不采用protobuf的方式来进行传输。
但是有时候你要传输的数据结构仅仅就是vector/unordered_map等动态数据结构的一些嵌套, 这时候去使用protobuf就稍嫌重量了。
这时其实可以结合C++的模板推导及参数重载来实现一个简易的数据序列化库。
大概试了一下vector,用起来还是很方便的。
template
serial(const T &a, std::string &res)
{
        res.append((char *)&a, sizeof(a));
}
template
unserial(T &a, const char *p)
{
        a = *(T *)p;
        return sizeof(T);
}
template
serial(const std::vector
{
        size_t n = a.size();
        res.append((char *)&n, sizeof(n));
        for (size_t i = 0; i < n; i++)
                serial(a[i], res);
}
template
unserial(std::vector
{
        size_t pos;
        size_t n = *(size_t *)p;
        a.reserve(n);
        pos = sizeof(size_t);
        for (size_t i = 0; i < n; i++) {
                T tmp;
                pos += unserial(tmp, &p[pos]);
                a.push_back(tmp);
        }
        return pos;
}
int main()
{
        std::vector
        std::vector
        std::string dst;
        serial(src1, dst);
        unserial(src2, &dst[0]);
for (size_t i = 0; i < src2.size(); i++) printf("%d ", src2[i]); return 0; }
从上面三个模板的例子上看,基本上模板就是强类型语言用来在写代码时弱化类型的一个折中。如果有过动态语言编写经验就会明显感觉到,模板明显是为了有限的支持动态语言在编写时的一些优点。如果能够把握这一点,也许才能够更合理,也更巧妙的去运用模板。