C++中的模板编程

一,函数模板

1.函数模板的概念

  • C++中提供了函数模板,所谓函数模板,实际上是建立一个通用函数,其函数的返回值类型和函数的参数类型不具体指定,用一个虚拟的类型来表示。这个通用函数就被称为函数的模板。
  • 当我们在开发中,经常会遇到一些函数体实现方式类似的函数,例如交换两个字符,交换两个数字函数,这两个函数的函数体实现是一样的,凡是这样的函数,我们都可以通过函数模板的方式来只定义一次。在调用函数时,系统会根据实参的数值类型来取代模板中的虚拟类型。再代入函数体中进行计算,这样就实现了定义一个函数模板,可以实现不同功能的函数。
  • 模板把函数或者类要处理的数据类型参数化,表现为参数的多态化。
  • 模板用于表达逻辑结构相同,但具体数据类型不同的通用行为。

2.函数模板的推演

# include<iostream>
using namespace std;

/* 交换两个整数 */
void myswap(int &a, int &b)
{
    int tmp = 0;
    tmp = a;
    a = b;
    b = tmp;
}

/* 交换两个字符 */
void myswap(char &a, char &b)
{
    char tmp;
    tmp = a;
    a = b;
    b = tmp;
}
/*
* 我们发现上面两个函数的实现逻辑结构相同,要处理的数据类型不同,
* 而函数模板恰恰是将数据类型参数的一种机制,所以此处我们在这里定义函数模板
*/
template<typename T>
void myswap(T &t1, T &t2)
{
    T tmp;
    tmp = t1;
    t1 = t2;
    t2 = tmp;
}

int main()
{
    int a = 10;
    int b = 20;

    char c = ‘C‘;
    char d = ‘D‘;

    myswap(a, b);
    myswap(c, d);

    cout << "a = " << a << ",b = " << b << endl;
    cout << "c = " << c << ",d = " << d << endl;

    /* 函数模板方式来处理 */
    myswap<int>(a, b);
    myswap<char>(c, d);

    cout << "a = " << a << ",b = " << b << endl;
    cout << "c = " << c << ",d = " << d << endl;

    return 0;
}

3.函数模板的形式

  在需要抽象化成模板的函数前面添加:template <typename T1,typename T2......>,在函数体内部要处理的数据类型定义为模板参数中的抽象数据类型即可。

4.函数模板的调用方式

  • 通过显式类型调用的方式,即myswap<int>(a,b)这种形式,显式的声明要处理的数据类型。
  • 通过数据类型自动推导的方式,即myswap(a,b)这种形式,模板函数会自动匹配传入参数的数据类型。

5.普通函数和函数模板在一起时调用规则

  • 函数模板不允许自动类型转换,普通函数可以进行自动类型转换,例如char会转换成int类型。
  • 函数模板可以向普通函数那样进行重载。
  • 编译器会优先使用普通函数,然后再使用函数模板。
  • 可以通过空模板实参列表的方式限定编译器只能通过模板匹配。myswap<>(a,b)

6.C++编译器模板机制剖析

  • 编译器并不是把函数模板处理成能够处理任意类的函数。
  • 编译器通过函数模板所传递的数据类型产生不同的函数。
  • 编译器对函数模板进行两次编译,在定义函数模板的地方对代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

二,类模板

1.为什么需要类模板

  类模板的含义与函数模板类似,所谓的类模板是在多个类中的逻辑操作和功能是相同的,仅仅数据的类型不同,我们将数据类型抽象化就形成了类模板。类模板通常用在数据结构和算法这一块,这样在存储数据和对数据进行操作的时候,可以抽象化要操作的数据。形成泛型编程。

2.单个类模板的语法

# include<iostream>
using namespace std;

/* 定义类模板 */
template<typename T>
class A
{
private:
    T t;
public:
    A(T t)
    {
        this->t = t;
    }
    void setT(T t)
    {
        this->t = t;
    }
    T getT()
    {
        return this->t;
    }
};

int main()
{
    A<int> a(10);
    int res1 = a.getT();
    cout << res1 << endl;

    a.setT(200);
    int res2 = a.getT();
    cout << res2 << endl;

    return 0;
}

3.继承中的类模板

# include<iostream>
using namespace std;
/* 类模板 */
template<typename T>
class Parent
{
private:
    T t;
public:
    Parent(T t)
    {
        this->t = t;
    }
    void getT()
    {
        cout << t << endl;
    }
};
/* 类模板继承中的子类为普通类 */
class Child1 :public Parent<int>
{
public:
    Child1(int t):Parent(t)
    {

    }
};
/* 类模板中继承的子类为类模板 */
template<typename T>
class Child2 :public Parent<T>
{
public:
    Child2(T t) :Parent(t)
    {

    }
};
int main()
{
    /* 子类为普通类 */
    Child1 c1(10);
    c1.getT();

    /* 子类为派生类 */
    Child2<char> c2(‘A‘);
    c2.getT();

    return 0;
}

4.类模板的定义和类模板的实现不在同一个文件中

  • 类模板中的友元函数要特别注意包含<T>,否则会报错,但是实现文件中不需要包含。
  • 在使用类模板的时候,引入的头文件是.cpp而不是.h,否则友元函数重载的操作符会报错。
  • 如果存储的数据是自定义的类,需要我们把自定义的类重写拷贝构造函数和重载赋值操作符才能保证元素的正确被复制。

三,类模板的应用

1.头文件(Vector.h)

# pragma once
# include<iostream>
using namespace std;

template<typename T>
class Vector
{
private:
    T * space;
    int size;
public:
    Vector(int size = 0);
    Vector(const Vector<T>& v);
    ~Vector();
public:
    T& operator[](int index);
    Vector<T>& operator=(Vector<T>& v);
public:
    friend ostream& operator<<<T>(ostream& out, Vector<T>& v);
};

2.实现文件(Vector.cpp)

# include<iostream>
# include"Vector.h"
using namespace std;

template<typename T>
Vector<T>::Vector(int size)
{
    this->space = new T[size];
    this->size = size;
}

template<typename T>
Vector<T>::Vector(const Vector<T>& v)
{
    this->space = new T[v.size];
    this->size = v.size;
    for (int i = 0; i < v.size; i++)
    {
        this->space[i] = v.space[i];
    }
}

template<typename T>
Vector<T>::~Vector()
{
    if (this->space != NULL)
    {
        delete[] this->space;
        this->space = NULL;
        this->size = 0;
    }
}

template<typename T>
T& Vector<T>::operator[](int index)
{
    return this->space[index];
}
template<typename T>
Vector<T>& Vector<T>::operator=(Vector<T>& v)
{
    if (this->space != NULL)
    {
        delete[] this->space;
        this->space = NULL;
        this->size = 0;
    }
    this->space = new T[v.size];
    this->size = v.size;
    for (int i = 0; i < v.size; i++)
    {
        this->space[i] = v.space[i];
    }
    return *this;
}

template<typename T>
ostream& operator<<(ostream& out, Vector<T>& v)
{
    for (int i = 0; i < v.size; i++)
    {
        out << v[i] << " ";
    }
    return out;
}

3.测试文件(Test.cpp)

# include<iostream>
# include"Vector.cpp"
using namespace std;

class Student
{
private:
    char * name;
    int age;
public:
    Student()
    {
        this->name = new char[1];
        strcpy(this->name, "");
        this->age = 0;
    }
    Student(const Student& stu)
    {
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        this->age = age;
    }
    Student(char * name, int age)
    {
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        this->age = age;
    }
    ~Student()
    {
        if (this->name != NULL)
        {
            delete[] this->name;
            this->name = NULL;
        }
    }
    Student& operator=(Student& stu)
    {
        if (this->name != NULL)
        {
            delete[] this->name;
            this->name = NULL;
        }
        this->name = new char[strlen(stu.name) + 1];
        strcpy(this->name, stu.name);
        this->age = stu.age;
        return *this;
    }
    friend ostream& operator<<(ostream& cout, Student& stu);
};

ostream& operator<<(ostream& cout, Student& stu)
{
    cout << "name:" << stu.name << ",age:" << stu.age << endl;
    return cout;
}

int main()
{
    Vector<int> v1(10);

    for (int i = 0; i < 10; i++)
    {
        v1[i] = i;
    }
    cout << v1 << endl;

    Vector<int> v2(5);

    for (int i = 0; i < 5; i++)
    {
        v2[i] = i;
    }
    cout << v2 << endl;

    v1 = v2;
    cout << v1 << endl;

    Vector<int> v3 = v1;
    cout << v3 << endl;

    Student t1("刘备", 49);
    Student t2("关羽", 38);
    Student t3("张飞", 34);

    Vector<Student> vTeacher(3);
    vTeacher[0] = t1;
    vTeacher[1] = t2;
    vTeacher[2] = t3;
    cout << vTeacher << endl;

    return 0;
}
时间: 2024-09-30 09:23:05

C++中的模板编程的相关文章

C++ 11可变参数接口设计在模板编程中应用的一点点总结

概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量(或属性,C#中属性和成员变量还是有区别的): 类的成员方法: 从编译器的角度看,我们必须明确指定以上3部分,才算完整地定义了一个类并且编译通过. 所谓的“类弱化”,是指类的设计者在定义类的时候,并没有完整定义一个类,而是把类的其中一部分的定义留给类的使用者. 从传统才c++98看,通过模板类,使用

C++模板编程中只特化模板类的一个成员函数

模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多数成员函数的功能可能是一模一样的,特化时我们可能只需要重新实现1.2个成员函数即可.在这种情况下,如果全部重写该模板类的所有成员函数,不但会增加工作量,也不利于代码的维护. 例如下面的类模板A,只有在模板参数是char*时才需要特化成员函数func(),但其他的成员函数都不需要特化: 1 template <typename _Ty> 2 struct A 3 { 4 // 其他成员函数a 5 //

C++模板编程中只特化模板类的一个成员函数(花样特化一个成员函数)

转自:https://www.cnblogs.com/zhoug2020/p/6581477.html 模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多数成员函数的功能可能是一模一样的,特化时我们可能只需要重新实现1.2个成员函数即可.在这种情况下,如果全部重写该模板类的所有成员函数,不但会增加工作量,也不利于代码的维护. 例如下面的类模板A,只有在模板参数是char*时才需要特化成员函数func(),但其他的成员函数都不需要特化: 1 templ

C++模板编程

如何处理函数模板中的函数体? 预备知识补充: 按照c++的语言系统,普通函数及类的声明应该放在一个头文件中(通常是.h. .hpp..hh为扩展名)里: 而将其实现放在一个主代码文件中(通常以.c ..cpp. .cc为扩展名)里:这样便于将代码分散编译到多个目标文件中,最后通过链接形成一个完整的目标文件: “头文件放声明. 主文件放实现” 但上述规则并不适用于模板: C++模板编程,布布扣,bubuko.com

[C++]C++的模板编程

我是搬运工,原文地址:http://www.cppblog.com/besterChen/archive/2010/07/22/121000.html 当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同而已. C++ 提供了“模板”这一特性, 可以将“类型” 参数化, 使得编写的代码更具有通用性. 因此大家都称模板编程为 “通用编程”或 “泛型编程

STL中的Traits编程技法

最近在看读<STL源码剖析>,看到Traits编程技法这节时,不禁感慨STL源码作者的创新能力.那么什么是Traits编程技法呢?且听我娓娓道来: 我们知道容器的许多操作都是通过迭代器展开的.其中容器类似于数组,迭代器类似于指针.我们用数组来写个例子: 1 int arr[5] = {1,2,3,4,5}; 2 int *p; 3 p = &arr[2]; 假设,我将第1.2遮挡起来,问你p所指向的对象arr[2]是什么类型,你恐怕无法回答.因为它可以是int,char,float甚至

C++的模板编程

当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同而已. C++ 提供了“模板”这一特性, 可以将“类型” 参数化, 使得编写的代码更具有通用性. 因此大家都称模板编程为 “通用编程”或 “泛型编程”. 一般而言, 模板分为 函数模板 和 类模板,下面就让我们分别来了解一下它们. 一. 函数模板 1. 函数模板的定义和使用 定义一个模板函数的格式并

虚函数和模板编程的一点共性和特征模板的一个例子

最近在看元编程中,对虚函数和模板编程有一点点感悟,写一篇博客简单总结一下. 虚函数和模板是C++里面很棒的特征,他们都提供了一种方法,让程序在编译中完成一些计算,去掉的这些计算在比较low的编程方式中,是需要在程序运行中执行的.在这里,我要强调的是:"在编译过程中完成一些计算". 我会举两个例子,一个是虚函数的,比较简单,另一个例子是关于特征模板的,在例子中,根据模板参数的类型自动选择模板的底层数据结构. 第一个例子是比较简单的虚函数的例子,有很多种水果的类型,我们有一个函数要展示他们

linux驱动程序中的异步编程

A 前面介绍的等待队列和轮询编程提供了较好的解决设备访问的机制,但是这些机制都 是由应用程序发起的,都需要应用程序主动访问设备.更完美的方式是由驱动程序主 动通知应用程序,也就是说,当驱动程序满足某些条件后,会主动通知应用程序处理 ,这些处理方式有些像面向对象编程的事件,而在linux内核使用的事件是接下来要介 绍的信号. #include<sys/types.h> #include<sys/stat.h> #include<stdio.h> #include<f