C++ 泛型编程

本文深入探讨了C++中的泛型编程,重点介绍了模板的使用,包括函数模板和类模板。函数模板允许创建适用于多种类型的通用函数,而类模板则使我们能够创建可以存储不同类型数据的类。文章还详细阐述了模板的重载、显式实例化和具体化。此外,还展示了如何使用迭代器,这是STL中的关键概念,用于遍历容器中的元素。通过示例代码,读者可以理解如何在实际编程中应用这些概念。
摘要由CSDN通过智能技术生成

STL是一种泛型编程(generic programming)。面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用代码,但它们的理念绝然不同。

模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。因此,它们都是STL 通用方法的重要组成部分。

1. 模板

1.1 Function templates

Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type.

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function. These function templates can use these parameters as if they were any other regular type.

The format for declaring function templates with type parameters is:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

The only difference between both prototypes is the use of either the keyword class or the keyword typename. Its use is indistinct, since both expressions have exactly the same meaning and behave exactly the same way.

For example, to create a template function that returns the greater one of two objects we could use:

template<class myType>
myType GetMax(myType a, myType b) {
    return (a > b ? a : b);
}

Here we have created a template function with myType as its template parameter. This template parameter represents a type that has not yet been specified, but that can be used in the template function as if it were a regular type. As you can see, the function template GetMax returns the greater of two parameters of this still-undefined type.

To use this function template we use the following format for the function call:

function_name <type> (parameters);

For example, to call GetMax to compare two integer values of type int we can write:

int x,y;
GetMax <int> (x,y);

When the compiler encounters this call to a template function, it uses the template to automatically generate a function replacing each appearance of myType by the type passed as the actual template parameter (int in this case) and then calls it. This process is automatically performed by the compiler and is invisible to the programmer.

We can also define function templates that accept more than one type parameter, simply by specifying more template parameters between the angle brackets. For example:

template<class T, class U>
T GetMin(T a, U b) {
    return (a < b ? a : b);
}
a. 重载的模板
template <typename T> // original template
void Swap(T &a, T &b);
template <typename T> // new template
void Swap(T *a, T *b, int n);
b. 显示具体化

显式具体化(explicit specialization) 其中包含所需的代码。当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。

C++98标准

  • 对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。
  • 显式具体化的原型和定义应以 template<>打头,并通过名称来指出类型。
  • 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
// non template function prototype
void Swap(job &, job &);
// template prototype
template <typename T>
void Swap(T &, T &);
// explicit specialization for the job type
template <> void Swap<job>(job &, job &);
c. 实例化与具体化

在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)。例如,在程序清单8.13见c++ primer plus)中,函数调用Swap(i, j)导致编译器生成Swap()的一个实例,该实例使用 int 类型。模板并非函数定义,但使用 int 的模板实例是函数定义。这种实例化方式被称为隐式实例化(implicit instantiation),因为编译器之所以知道需要进行定义,是由于程序调用Swap()函数时提供了int参数。
最初,编译器只能通过隐式实例化,来使用模板生成函数定义,但现在C++还允许显式实例化(explicit instantiation)。这意味着可以直接命令编译器创建特定的实例,如 Swap<int>()。其语法是,声明所需的种类——用<>符号指示类型,并在声明前加上关键字template:

template void Swap<int>(int, int); // explicit instantiation

实现了这种特性的编译器看到上述声明后,将使用Swap()模板生成一个使用 int 类型的实例。也就是说,该声明的意思是“使用Swap()模板生成int类型的函数定义。”
与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:

template <> void Swap<int>(int &, int &); // explicit specialization
template <> void Swap(int &, int &); // explicit specialization

区别在于,这些声明的意思是“不要使用Swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义”。这些原型必须有自己的函数定义。显式具体化声明在关键字template后包含<>,而显式实例化没有。
警告:试图在同一个文件(或转换单元)中使用同一种类型的显式实例和显式具体化将出错。

1.2 Class templates

We also have the possibility to write class templates, so that a class can have members that use template parameters as types. For example:

template<class T>
class mypair {
    T values[2];
public:
    mypair(T first, T second) {
        values[0] = first;
        values[1] = second;
    }
};

The class that we have just defined serves to store two elements of any valid type. For example, if we wanted to declare an object of this class to store two integer values of type int with the values 115 and 36 we would write:

mypair<int> myobject (115, 36);

this same class would also be used to create an object to store any other type:

mypair<double> myfloats (3.0, 2.18);
a. 使用示例

myVector.h

#ifndef MYVECTOR_H_
#define MYVECTOR_H_


typedef int Rank; //秩
#define DEFAULT_CAPACITY  3 //默认的初始容量(实际应用中可设置为更大)

template<typename T>
class Vector { //向量模板类
protected:
    Rank _size;
    int _capacity;
    T *_elem; //规模、容量、数据区
    void copyFrom(T const *A, Rank lo, Rank hi); //复制数组区间A[lo, hi)
    void expand(); //空间不足时扩容
    void shrink(); //装填因子过小时压缩
    Rank bubble(Rank lo, Rank hi); //扫描交换
    void bubbleSort_v0(Rank lo, Rank hi); //起泡排序算法 (低效版)
    void bubbleSort(Rank lo, Rank hi); //起泡排序算法
    Rank maxItem(Rank lo, Rank hi); //选取最大元素
    void selectionSort(Rank lo, Rank hi); //选择排序算法
    void merge(Rank lo, Rank mi, Rank hi); //归并算法
    void mergeSort(Rank lo, Rank hi); //归并排序算法
    void heapSort(Rank lo, Rank hi); //堆排序
    Rank partition(Rank lo, Rank hi); //轴点构造算法
    void quickSort(Rank lo, Rank hi); //快速排序算法
    void shellSort(Rank lo, Rank hi); //希尔排序算法
public:
// 构造函数
    explicit Vector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0) { //容量为c、规模为s、所有元素初始为v
        _elem = new T[_capacity = c];
        for (_size = 0; _size < s; _elem[_size++] = v);
    } //s<=c
    Vector(T const *A, Rank n) { copyFrom(A, 0, n); } //数组整体复制
    Vector(T const *A, Rank lo, Rank hi) { copyFrom(A, lo, hi); } //区间
    Vector(Vector<T> const &V) { copyFrom(V._elem, 0, V._size); } //向量整体复制
    Vector(Vector<T> const &V, Rank lo, Rank hi) { copyFrom(V._elem, lo, hi); } //区间
// 析构函数
    ~Vector() { delete[] _elem; } //释放内部空间
// 只读访问接口
    Rank size() const { return _size; } //规模
    bool empty() const { return !_size; } //判空
    Rank find(T const &e) const { return find(e, 0, _size); } //无序向量整体查找
    Rank find(T const &e, Rank lo, Rank hi) const; //无序向量区间查找
    Rank search(T const &e) const { //有序向量整体查找
        return (0 >= _size) ? -1 : search(e, 0, _size);
    }

    Rank search(T const &e, Rank lo, Rank hi) const; //有序向量区间查找
    int disordered() const;  // 统计向量中的逆序相邻元素对

// 可写访问接口
    T &operator[](Rank r); //重载下标操作符,可以类似于数组形式引用各元素
    const T &operator[](Rank r) const; //仅限于做右值的重载版本
    Vector<T> &operator=(Vector<T> const &); //重载赋值操作符,以便直接克隆向量
    T remove(Rank r); //删除秩为r的元素
    int remove(Rank lo, Rank hi); //删除秩在区间[lo, hi)之内的元素
    Rank insert(Rank r, T const &e); //插入元素
    Rank insert(T const &e) { return insert(_size, e); } //默认作为末元素插入
    void sort(Rank lo, Rank hi, int method = 1); //对[lo, hi)排序
    void sort(int method = 1) { sort(0, _size, method); } //整体排序
    void unsort(Rank lo, Rank hi); //对[lo, hi)置乱
    void unsort() { unsort(0, _size); } //整体置乱
    int deduplicate(); //无序去重
    int uniquify(); //有序去重
// 遍历
    void traverse(void (* )(T &)); //遍历(使用函数指针,只读或局部性修改)
    template<typename VST>
    void traverse(VST &); //遍历(使用函数对象,可全局性修改)
}; //Vector


class Fib {
private:
    Rank f, g;
public:
    explicit Fib(Rank n) {
        f = 1, g = 0;
        while (g < n)
            next();
    }

    Rank get() const { return g; }

    Rank next() {
        g += f, f = g - f;
        return g;
    }

    Rank prev() {
        f = g - f, g -= f;
        return g;
    }
};

#endif //MYVECTOR_H_

myVector.cpp

#include"myVector.h"
#include<iostream>
#include<cassert>

// prototype
template<typename T>
static Rank binSearch_v0(T *A, T const &e, Rank lo, Rank hi);

template<typename T>
static Rank fibSearch_v0(T *A, T const &e, Rank lo, Rank hi);


template<typename T>
static Rank binSearch(T *A, T const &e, Rank lo, Rank hi);

// definition
template<typename T>
static Rank binSearch_v0(T *A, T const &e, Rank lo, Rank hi) {
    while (lo < hi) {
        Rank mi = (lo + hi) >> 1;
        if (e < A[mi])
            hi = mi;
        else if (A[mi] < e)
            lo = mi + 1;
        else
            return mi;
    }
    return -1;
}


template<typename T>
static Rank fibSearch_v0(T *A, T const &e, Rank lo, Rank hi) {
    Fib fib(hi - lo);
    while (lo < hi) {
        while (hi - lo < fib.get()) fib.prev();
        Rank mi = lo + fib.get() - 1;
        if (e < A[mi])
            hi = mi;
        else if (A[mi] < e)
            lo = mi + 1;
        else
            return mi;
    }
    return -1;
}

template<typename T>
static Rank binSearch(T *A, T const &e, Rank lo, Rank hi) {
    while (lo < hi) {
        Rank mi = (lo + hi) >> 1;
        e < A[mi] ? hi = mi : lo = mi + 1;
    }  // 出口时, A[lo = hi]为大于e的最小元素
    return --lo;
}


// protected
template<typename T>
void Vector<T>::copyFrom(const T *A, Rank lo, Rank hi) {
    _elem = new T[_capacity = 2 * (hi - lo)];
    _size = 0;
    while (lo < hi)
        _elem[_size++] = A[lo++];
}


template<typename T>
void Vector<T>::expand() {  //向量空间不足时扩容
    if (_size < _capacity) return;  //尚未满员时,不必扩容
    _capacity = std::max(_capacity, DEFAULT_CAPACITY);  //不低于最小容量
    T *oldElem = _elem;  //容量加倍
    _elem = new T[_capacity << 1];
    for (int i; i < _size; i++)  //复制原向量内容(T为基本类型,或已重载赋值操作符'=')
        _elem[i] = oldElem[i];
    delete[] oldElem;  //释放原空间
}

template<typename T>
void Vector<T>::shrink() { //装填因子过小时压缩向量所占空间
    if (_capacity < DEFAULT_CAPACITY << 1) return; //不致收缩到DEFAULT_CAPACITY以下
    if (_size << 2 > _capacity) return; //以25%为界
    T *oldElem = _elem;
    _elem = new T[_capacity >>= 1]; //容量减半
    for (int i = 0; i < _size; i++)
        _elem[i] = oldElem[i]; //复制原向量内容
    delete[] oldElem; //释放原空间
}

// protected - sort

template<typename T>
Rank Vector<T>::bubble(Rank lo, Rank hi) {
    Rank last = lo;
    while (++lo < hi)
        if (_elem[lo - 1] > _elem[lo]) {
            std::swap(_elem[lo - 1], _elem[lo]);
            last = lo;
        }
    return last;
}

template<typename T>
void Vector<T>::bubbleSort_v0(Rank lo, Rank hi) {
    for (bool sorted = false; sorted = (!sorted); hi--)
        for (int i = lo + 1; i < hi; i++)
            if (_elem[i - 1] > _elem[i]) {
                std::swap(_elem[i - 1], _elem[i]);
                sorted = false;
            }
}

template<typename T>
void Vector<T>::bubbleSort(Rank lo, Rank hi) {
    while (lo < hi)
        hi = bubble(lo, hi);
}


template<typename T>
void Vector<T>::merge(Rank lo, Rank mi, Rank hi) { //[lo, mi)和[mi, hi)各自有序,lo < mi < hi
    Rank i = 0;
    T *A = _elem + lo; //合并后的有序向量A[0, hi - lo) = _elem[lo, hi),就地
    Rank j = 0, lb = mi - lo;
    T *B = new T[lb]; //前子向量B[0, lb) <-- _elem[lo, mi)
    for (Rank i = 0; i < lb; i++) B[i] = A[i]; //复制自A的前缀
    Rank k = 0, lc = hi - mi;
    T *C = _elem + mi; //后子向量C[0, lc) = _elem[mi, hi),就地
    while ((j < lb) && (k < lc)) //反复地比较B、C的首元素
        A[i++] = (B[j] <= C[k]) ? B[j++] : C[k++]; //将更小者归入A中
    while (j < lb) //若C先耗尽,则
        A[i++] = B[j++]; //将B残余的后缀归入A中——若B先耗尽呢?
    delete[] B; //释放临时空间:mergeSort()过程中,如何避免此类反复的new/delete?
}

template<typename T>
void Vector<T>::mergeSort(Rank lo, Rank hi) { //0 <= lo < hi <= size
    /*DSA*/ //printf ( "\tMERGEsort [%3d, %3d)\n", lo , hi );
    if (hi - lo < 2) return; //单元素区间自然有序,否则...
    int mi = (lo + hi) / 2; //以中点为界
    mergeSort(lo, mi);
    mergeSort(mi, hi); //分别排序
    merge(lo, mi, hi); //归并
}

template<typename T>
Rank Vector<T>::maxItem(Rank lo, Rank hi) { //在[lo, hi]内找出最大者
    Rank mx = hi;
    while (lo < hi--) //逆向扫描
        if (_elem[hi] > _elem[mx]) //且严格比较
            mx = hi; //故能在max有多个时保证后者优先,进而保证selectionSort稳定
    return mx;
}

template<typename T>
void Vector<T>::selectionSort(Rank lo, Rank hi) {
    while (lo < --hi)
        std::swap(_elem[hi], _elem[maxItem(lo, hi)]);
}

// public - member function

template<typename T>
T &Vector<T>::operator[](Rank r) {
    assert((0 <= r) && (r <= _size));
    return _elem[r];
}

template<typename T>
const T &Vector<T>::operator[](Rank r) const {
    assert((0 <= r) && (r <= _size));
    return _elem[r];
}

template<typename T>
Vector<T> &Vector<T>::operator=(Vector<T> const &V) { //深复制
    if (_elem) delete[] _elem; //释放原有内容
    copyFrom(V._elem, 0, V.size()); //整体复制
    return *this; //返回当前对象的引用,以便链式赋值
}

template<typename T>
int Vector<T>::remove(Rank lo, Rank hi) {
    if (lo == hi) return 0; //出于效率考虑,单独处理退化情况,比如remove(0, 0)
    while (hi < _size) //区间[hi, _size)
        _elem[lo++] = _elem[hi++]; //顺次前移hi - lo个单元
    _size = lo; //更新规模,直接丢弃尾部[lo, _size = hi)区间
    shrink(); //若有必要,则缩容
    return hi - lo; //返回被删除元素的数目
}

template<typename T>
T Vector<T>::remove(Rank r) { //删除向量中秩为r的元素,0 <= r < size
    T e = _elem[r]; //备份被删除元素
    remove(r, r + 1); //调用区间删除算法,等效于对区间[r, r + 1)的删除
    return e; //返回被删除元素
}

template<typename T>
Rank Vector<T>::find(T const &e, Rank lo, Rank hi) const {
    assert((0 <= lo) && (lo <= _size));
    assert((0 <= hi) && (hi <= _size));
    while ((lo < hi--) && _elem[hi] != e);
    return hi;
}

template<typename T>
Rank Vector<T>::insert(Rank r, T const &e) { // 将e作为秩为r元素插入
    assert((0 <= r) && (r <= _size));
    expand(); //若有必要,扩容
    for (int i = _size; i > r; i--) //自后向前,后继元素
        _elem[i] = _elem[i - 1]; //顺次后移一个单元
    _elem[r] = e;
    _size++; //置入新元素并更新容量
    return r; //返回秩
}

template<typename T>
int Vector<T>::deduplicate() {
    int oldSize = _size; //记录原规模
    Rank i = 1;
    while (i < _size)
        (find(_elem[i], 0, i) == -1) ? i++ : remove(i);
    return oldSize - _size; //被删除元素总数
}

template<typename T>
int Vector<T>::uniquify() {
    Rank lo = 0, hi = 0;
    while (++hi < _size)
        if (_elem[hi] != _elem[lo])
            _elem[++lo] = _elem[hi];
    _size = ++lo;
    shrink();
    return hi - lo;  //向量规模变化量,即被删除元素总数
}


template<typename T>
void Vector<T>::traverse(void ( *visit )(T &)) {  // 借助函数指针机制
    for (int i = 0; i < _size; i++)
        visit(_elem[i]);
}  // 遍历向量

template<typename T>
template<typename VST>
void Vector<T>::traverse(VST &visit) {  // 元素类型、操作器 借助函数对象机制
    for (int i = 0; i < _size; i++)
        visit(_elem[i]);
}  // 遍历向量


template<typename T>
int Vector<T>::disordered() const {
    int count, i = 1;
    while (i++ < _size)
        count += (_elem[i - 1] == _elem[i]);
    return count;
}

template<typename T>
Rank Vector<T>::search(T const &e, Rank lo, Rank hi) const {  // 在有序向量的区间[lo, hi)内,确定不大于e的最后一个节点的秩
    assert((0 <= lo) && (lo <= _size));
    assert((0 <= hi) && (hi <= _size));
    return binSearch(_elem, e, lo, hi);
}

template<typename T>
void Vector<T>::sort(Rank lo, Rank hi, int method) { //向量区间[lo, hi)排序
    switch (method) {
        case 0:
            bubbleSort_v0(lo, hi);
            break; //起泡排序 (低效版)
        case 1:
            bubbleSort(lo, hi);
            break; //起泡排序
        case 2:
            selectionSort(lo, hi);
            break; //选择排序(习题)
        case 3:
            mergeSort(lo, hi);
            break; //归并排序
    } //随机选择算法以充分测试。实用时可视具体问题的特点灵活确定或扩充
}


template<typename T>
void Vector<T>::unsort(Rank lo, Rank hi) { //等概率随机置乱区间[lo, hi)
    T *V = _elem + lo; //将子向量_elem[lo, hi)视作另一向量V[0, hi - lo)
    for (Rank i = hi - lo; i > 0; i--) //自后向前
        std::swap(V[i - 1], V[rand() % i]); //将V[i - 1]与V[0, i)中某一元素随机交换
}

2. 迭代器

2.1 使用示例
// std::iterator example
#include <iostream>     // std::cout
#include <iterator>     // std::iterator, std::input_iterator_tag

class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
  int* p;
public:
  MyIterator(int* x) :p(x) {}
  MyIterator(const MyIterator& mit) : p(mit.p) {}
  MyIterator& operator++() {++p;return *this;}
  MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
  bool operator==(const MyIterator& rhs) const {return p==rhs.p;}
  bool operator!=(const MyIterator& rhs) const {return p!=rhs.p;}
  int& operator*() {return *p;}
};

int main () {
  int numbers[]={10,20,30,40,50};
  MyIterator from(numbers);
  MyIterator until(numbers+5);
  for (MyIterator it=from; it!=until; it++)
    std::cout << *it << ' ';
  std::cout << '\n';

  return 0;
}

参考资料

[1] C++.Primer.Plus.6th.Edition

[2] C++ Reference

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值