C++11泛型 - 类模板

前面我们介绍了函数模板。今天我们来看看C++的另一种泛型:类模板。C++中类模板通常是容器(如std::vector)或行为的封装(如之前我们实现的chan<T>类)。类模板语法:

template < parameter-list > class-declaration

构成类模板的形参(parameter-list)约束与函数模板相同,此处就不赘述了。与函数模板的类型自动推演不同,类模板实例化时,需要显式指定:

std::vector<int> intArr;

一、成员函数

与函数模板一样,类模板只是定义了一组通用的操作,在具体实例化前是不占用程序空间的。这种Lazy特性在类模板中得到了进一步地加强:成员函数(含成员函数模板)只有在使用时才生成

template<typename T>
class A {
    T a_;
public:
    void add(int n) {
        a_ += n;
    }
};

class M{};

int main() {
    A<M> s;      // s未用到add函数,因此整个程序得以成功编译
   // s.add(1);  // 如果有这句话,则会编译失败
}

本例中,A<T>::add在变量s中未使用到,因此虽然a_ += n不合法,但整个程序仍然通过了编译。

1.1 虚函数

在函数模板中我们提到虚函数不能是函数模板,那么在类模板中可以有虚函数吗?答案是肯定的,类模板中可以有虚函数,虚函数在类模板实例化为模板类时由编译器生成,因此其实现必须是合法的,否则即使未被使用到,也会编译失败。类模板的虚函数可以访问模板类中的泛型成员(变量、成员函数模板都可以访问)。

#include <iostream>
template<typename T>
class A {
    T a_;
public:
    virtual void say() {
        std::cout << "a -> " << a_ << std::endl;
    }
};

class M{};

int main() {
    // 尽管say函数未被使用,此处会编译仍会失败,因为std::cout << m.a_操作是非法的
    A<M> m;
}

1.2 成员函数模板

类模板和函数模板结合就是成员函数模板。

#include <iostream>
template<typename T>
class Printer {
    T prefix_;
public:
    explicit Printer(const T &prefix):prefix_(prefix){
    }
    // 成员函数模板
    template<typename U, typename ...Args> void print(const U &u, Args... args);
    void print() {
        std::cout << std::endl;
    }
};

template<typename T> template<typename U, typename ...Args>
void Printer<T>::print(const U &u, Args... args) {
    std::cout << this->prefix_ << u << std::endl;
    print(args...);
}

二、类模板特化与偏特化

模板特化是指定类模板的特定实现。是针对某类型参数的特殊化处理。假设我们有一个类模板Stack<T>,它有一个功能:min(取Stack的最小值),则该类模板的典型实现如下:

template<typename T>
struct StackItem {
    StackItem *next;
    T item;
};
template<typename T>
class Stack {
    StackItem<T> *front = nullptr;
public:
    T min() const {
        assert(front != nullptr);
        T min = front->item;
        for (StackItem<T> *it = front->next; it != nullptr; it = it->next) {
            if (it->item < min) {
                min = it->item;
            }
        }
        return min;
    }
};

Stack<T>::min所需满足的契约是:T需支持小于操作(operator <)。但有些类型无法满足该要求,如const char *。如果Stack<T>要支持const char *的话,则需要特化。

template<>
class Stack<const char *>  // 类名后面,跟上<...>,则表明是特化
{
    StackItem<const char *> *front = nullptr;
public:
    const char * min() const {
        assert(front != nullptr);
        const char * min = front->item;
        for (StackItem<const char *> *it = front->next; it != nullptr; it = it->next) {
            if (strcmp(it->item, min) < 0) {
                min = it->item;
            }
        }
        return min;
    }
};

2.1 偏特化

偏特化也叫部分特化,指的是当类模板有一个以上模板参数时,我们希望能对某个或某几个模板实参进行特化。类模板的特化(或偏特化)只需要模板名称相同并且特化列表<>中的参数个数与原始模板对应上即可,模板参数列表不必与原始模板相同模板名称相同。一个类模板可以有多个特化,与函数模板相同,编译器会自动实例化那个最特殊的版本。

类模板特化和偏特化

完全特化的结果是一个实际的class,而偏特化的结果是另外一个同名的模板。

三、类模板中的static成员

类模板中可以声明static成员。但需要注意的是每个不同模板实例都会有一个独立的static成员变量

template<typename T>
class A {
public:
    static int count;
};
template<typename T> int A<T>::count = 1;

A<int>::countA<double>::count是不同的两个变量。

3.1 类模板中static成员的特化

static成员也可以进行特化

// template<typename T> A {...};  // 见上面的定义
template<> int A<const char *>::count = 100;

则A<const char *>::count的值被值始化为100,而以其它类型进行实例化时则初始化为1。

四、友元

友元在C++中做为一个BUG式的存在,可以授权“好友”访问其隐私数据。

template<typename T> class A;  //  前置声明,在B中声明友元需要的

template<typename U>
class B {
    // 每个B的实例将授权相同类型实例化的A
    friend class A<U>;
    // C的所有实例都是B的友元,该种情况下C无需前置声明
    template<typename T> fiend class C;
    // 普通类
    friend class D;
    // 模板自己的类型参数成为友元
    friend U;
};

五、总结

本节简单介绍了类模板,由于篇幅限制不能一一展开,如有疏漏欢迎批评指正。

上一篇
C++11多线程-函数模板
目录 下一篇
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,373评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,732评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,163评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,700评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,036评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,425评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,737评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,421评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,141评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,398评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,908评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,276评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,907评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,018评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,772评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,448评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,325评论 2 261

推荐阅读更多精彩内容