泛型编程之模版

本文深入探讨了C++中的泛型编程,主要围绕模板展开,包括函数模板、类模板和成员模板。函数模板允许编译器根据实参类型推断模板参数,减少代码重复。类模板则用于生成类的蓝图,需要显式指定模板参数。成员模板是类中的模板函数,无法由编译器推断模板参数。通过模板,程序员可以实现更加通用和灵活的代码设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型编程

面向对象编程(OOP)和泛型编程都能处理在编写程序时不知道类型的情况。不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译时就能获知类型了。(这句话还得慢慢去理解)

模板是C++中泛型编程的基础。一个模板就是一个创建类或者函数的蓝图或者说公式。当使用一个vector这样的泛型类型,或者find这样的泛型函数时,我们提供足够的信息,将蓝图转换为特定的类或函数。

1 函数模板

函数模板的定义

假定我们希望编写一个函数来比较两个变量的大小,在实际中,由于变量的类型不同,我们可能需要定义多个重载函数,比如下面两种:

//比较两个变量v1,v2的大小,相等返回0,v1大返回1,v2大返回-1
int compare(const string &v1, const string &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

int compare(const double &v1, const double &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

这两个函数几乎是相同的,唯一的差异是参数的类型,函数体完全一样。为了避免重复定义,我们可以定义一个通用的函数模板(function template),compare的函数模板可以定义如下:

template <typename T>
int compare(const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

函数模板需要注意一下几点:

  • 在模板定义中,模板参数列表(也就是尖括号里面的部分)不能为空。
  • 类型参数(也就是T)前必须使用关键字class或者typename(两者可以互换使用)。
函数模板的实例化

当我们调用一个模板函数时,编译器可以通过函数实参来为我们推断模板实参的类型。比如在下面的调用中:

cout <<  compare(1, 0) << endl;	//T为int

实参类型为int,编译器会推断出模板实参为int,并将它绑定到模板参数T。

编译器也会用推断出的模板参数来为我们 实例化(instantiate) 一个特定版本的函数,如下:

int compare(const int &v1, const int &v2)
{
	if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

这些编译器生成的版本通常被成为模板的实例(instantiation)

2 类模板

类模板(class template) 是用来生成类的蓝图的。与函数模板不同的是,编译器不能为类模板推断模板参数类型。为了使用类模板,我们必须在模板名之后的尖括号提供额外信息(比如vector<int>)。

类模板的定义

假定我们希望定义一个名为Blob的模板,保存一组元素。与容器不同,我们希望Blob对象的不同拷贝之间共享相同的元素。即,当我们拷贝一个Blob时,原Blob对象及其拷贝应该引用相同的底层元素。Blob模板定义如下:

template <typename T> class Blob {
public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    // 构造函数
    Blob();
    Blob(std::initializer_list<T> il);
    // Blob中的元素数目
    size_type size() const { return data->size(); }
    bool empty() const { return data->empty(); }
    // 添加和删除元素
    void push_back(const T &t) { data->push_back(t);}
    // 移动版本
    void push_back(T &&t) { data->push_back(std::move(t)); }
    void pop_back();
    T& back();
    T& operator[](size_type i);
private:
    std::shared_ptr<std::vector<T>> data;
    // 若data[i]无效,则抛出msg
    void check(size_type i, const std::string &msg) const;
};
类模板的实例化

当使用一个类模板时,我们必须提供额外信息,即显式模板实参(explicit template argument) 列表,它们被绑定模板参数。编译器使用这些模板实参来实例化出特定的类。比如,下面是Blob模板实例化的一个例子:

Blob<int> ia;									//空Blob<int>
Blob<int> ia2 = {0, 1, 2, 3, 4}		//有5个元素的Blob<int>

3 成员模板

一个类(无论是普通类还是类模板)可以包含本身是模板的成员函数。这种成员被称为成员模板(member template)。成员模板不能是虚函数。

普通类的成员模板
class DebugDelete {
public:
    DebugDelete(std::ostream &s = std::cerr) : os(s) { }
    // 与任何函数模板相同,T的类型由编译器推断
    template <typename T> void operator()(T *p) const { os << "deleting unique_ptr"} << std::endl;  delete p; }
private:
	std::ostream &os;
};
类模板的成员模板
template <typename T> class Blob {
    template <typename It> Blob(It b, It e);
    //...
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值