初识C++模板

1 引入模板

        实现一个算法:返回两个变量中的较大的一个,并支持不同的数据类型。如果不使用C++模板,会有哪些选择呢?

        根据数据类型一遍一遍实现算法

int max_int(int x, int y) {
    return x > y ? x : y;
}

int max_double(double x, double y) {
    return x > y ? x : y;
}

        使用void *指针,然后指定比较函数

typedef const void *(*cmp)(const void *, const void *);

const void *max_cmp(const void *x, const void *y, cmp f)
{
    return f(x, y);
}

const void *cmp_int(const void *x, const void *y) {
    const int *ix = (const int *)x;
    const int *iy = (const int *)y;
    return *ix > *iy ? x : y;
}

const void *cmp_double(const void *x, const void *y) {
    const double *dx = (const double *)x;
    const double *dy = (const double *)y;
    return *dx > *dy ? x : y;
}

        使用继承,定义一个接口类,由派生类实现比较函数

class base
{
public:
    virtual bool max_than(const base &rhs) const = 0;
};

class I : public base {
public:
    I(int i) : m_data(i) {}
    virtual bool max_than(const base &rhs) const {
        const I &other = dynamic_cast(rhs);
        return m_data > other.m_data;
    }
    
public:
    int m_data;
};

class D : public base {
public:
    D(int d) : m_data(d) {}
    virtual bool max_than(const base &rhs) const {
        const D &other = dynamic_cast(rhs);
        return m_data > other.m_data;
    }
    
public:
    double m_data;
};

const base &max_class(const base &x, const base &y) {
    return x.max_than(y) ? x : y;
}

        仔细观察,会发现三种方案都存在不同程度的冗余。

        方案一冗余程度最高,没有任何复用可言。这种方案不仅增加了工作量,还增加了维护成本。每当需要支持一种新的数据类型,都需要重新实现一遍算法;如果算法更新,还需要同步到各个实现。

        方案二和方案三的本质是一样的,对原有数据类型进行封装,通过回调方式解决数据类型差异问题。不同之处在于方案二使用void指针实现,存在严重的类型安全问题(但这也是C语言中的唯一选择);方案使用继承,通过基类引用取代了void指针。

        通过与上面这些方案对比,使用模板实现这个算法就简单的多了,如下:

#include 
#include 
#include 

template 
T max_(T x, T y)
{
        return x > y ? x : y;
}

int main(int argc, char **argv)
{
        printf("%d\n", max_(10, 20));
        printf("%f\n", max_(10.1, 20.2));
        std::string s1("a");
        std::string s2("b");
        printf("%s\n", max_(s1, s2).c_str());

        return 0;
}

2 模板的本质 

         模板的本质是一种c++多态,又称编译时多态。尽管源码中仅有一个函数,但却实现了对多种类型的支持。为什么会有如此神奇的事情?因为C++编译器有一个神奇的技能——无中生有。通过“objdump --syms template | grep max_”查看可执行程序的符号表,程序中确实包含三个名称包含max_的函数,如下:

        可执行程序中的三个函数,与源码中的max_函数是什么关系呢?不妨做个实验。

        实验一数据类型为double的调用实例,发现可执行程序中名称包含max_的函数少了一个;

#include 
#include 
#include 

template 
T max_(T x, T y)
{
        return x > y ? x : y;
}

int main(int argc, char **argv)
{
        printf("%d\n", max_(10, 20));
        std::string s1("a");
        std::string s2("b");
        printf("%s\n", max_(s1, s2).c_str());

        return 0;
}

        增加一行数据类型为int的调用实例,发现可执行程序中名称包含max_的函数并未增加;

#include 
#include 
#include 

template 
T max_(T x, T y)
{
        return x > y ? x : y;
}

int main(int argc, char **argv)
{
        printf("%d\n", max_(10, 20));
        printf("%d\n", max_(50, 10));
        std::string s1("a");
        std::string s2("b");
        printf("%s\n", max_(s1, s2).c_str());

        return 0;
}

        从上面的实验推出,名称中包含max_的函数的生成,仅与实例的类型相关,与调用次数无关。但现在还不能推出可执行程序名称包含max_函数,就是源码中的max_函数。

        再做一个实验,使用“g++ -S template.cpp -o template.s”生成汇编代码,仔细研究template.s(代码太长,此处不在粘贴),发现可执行程序名称包含max_函数,就是源码中的max_函数。在代码的编译阶段,g++为max生成了三个不同的实例版本。

        由此可见,模板也并非免费的午餐:增加编译时间;增加生成程序的体积。但与其所提升的开发效率相比,这些可以忽略不计。

你可能感兴趣的:(c++,模板)