一,函数模板

1.函数模板的概念

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

2.函数模板的推演

# include<iostream>
using namespace std; /* 交换两个整数 */
void myswap(int &a, int &b)
{
int tmp = ;
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 = ;
int b = ; 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 ;
}

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();
int res1 = a.getT();
cout << res1 << endl; a.setT();
int res2 = a.getT();
cout << res2 << endl; return ;
}

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();
c1.getT(); /* 子类为派生类 */
Child2<char> c2('A');
c2.getT(); return ;
}

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 = );
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 = ; 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 = ;
}
} 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 = ;
}
this->space = new T[v.size];
this->size = v.size;
for (int i = ; 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 = ; 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[];
strcpy(this->name, "");
this->age = ;
}
Student(const Student& stu)
{
this->name = new char[strlen(name) + ];
strcpy(this->name, name);
this->age = age;
}
Student(char * name, int age)
{
this->name = new char[strlen(name) + ];
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) + ];
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(); for (int i = ; i < ; i++)
{
v1[i] = i;
}
cout << v1 << endl; Vector<int> v2(); for (int i = ; i < ; i++)
{
v2[i] = i;
}
cout << v2 << endl; v1 = v2;
cout << v1 << endl; Vector<int> v3 = v1;
cout << v3 << endl; Student t1("刘备", );
Student t2("关羽", );
Student t3("张飞", ); Vector<Student> vTeacher();
vTeacher[] = t1;
vTeacher[] = t2;
vTeacher[] = t3;
cout << vTeacher << endl; return ;
}

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

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

    概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量 ...

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

    模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多数成员函数的功能可能是一模一样的,特化时我们可能只需要重新实现1.2个成员函数即可.在这种情况下,如果全 ...

  3. C#中的函数式编程:递归与纯函数(二) 学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面

    C#中的函数式编程:递归与纯函数(二)   在序言中,我们提到函数式编程的两大特征:无副作用.函数是第一公民.现在,我们先来深入第一个特征:无副作用. 无副作用是通过引用透明(Referential ...

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

    转自:https://www.cnblogs.com/zhoug2020/p/6581477.html 模板编程中如果要特化或偏特化(局部特化)一个类模板,需要特化该类模板的所有成员函数.类模板中大多 ...

  5. C++之模板编程

    当我们越来越多的使用C++的特性, 将越来越多的问题和事物抽象成对象时, 我们不难发现:很多对象都具有共性. 比如 数值可以增加.减少:字符串也可以增加减少. 它们的动作是相似的, 只是对象的类型不同 ...

  6. UE4 中的 C++ 编程介绍

    https://docs.unrealengine.com/latest/CHN/Programming/Introduction/index.html UE4 中的 C++ 编程介绍 Unreal ...

  7. c++ 基于Policy 的 模板编程

    在没真正接触c++  模板编程之前.真的没有想到c++ 还能够这么用.最大的感触是:太灵活了,太强大了. 最初接触模板威力还是在Delta3d中,感觉里面的模板使用实在是灵活与方便,特别是dtAI中使 ...

  8. C++中函数模板的概念和意义

    1,对泛型编程进行学习,泛型编程是实际工程开发中必用的技术,大型公司的通用 库都是采用泛型编程的技术完成的,C++ 中支持泛型编程技术,C++ 中的函数  模板和类模板就是 C++ 中泛型编程技术,本 ...

  9. C++中函数模板的深入理解

    1,函数模板深入理解: 1,编译器从函数模板通过具体类型产生不同的函数: 1,模板就是模子,通过这个模子可以产生很多的实物: 2,函数模板就是编译器用来产生具体函数的模子: 2,编译器会对函数模板进行 ...

随机推荐

  1. 把git仓库从码云迁到github,及git常用命令

    前言 刚开始建仓库的时候,因为网络的原因选择了国内的码云.后来又想换成github,毕竟平时github使用率比较高. 替换远程仓库地址方式如下: git remote set-url origin ...

  2. Delegates, Events and Singletons with Unity3D – C#

     在这里我将演示怎样创建代表. 事件和Singletons 在一起工作. 本教程为 Unity3D 编写. 我想知道这为什么?作为一个年轻的自学程序猿,我常常发现自己写tons 和布尔的语句,以确 ...

  3. Java反射学习总结二(用反射调用对象的私有属性和方法)

    大家都知道正常的调用是不可以访问对象的private修饰的属性和方法的,这也是Java的封装性原则. 但是有没有方法可以强制去访问对象的private修饰的属性和方法呢?那就是用反射!(这个可能在面试 ...

  4. Android Studio2.0 Beta 2版本号更新说明及注意事项

    我们刚刚向canary channel推送了Android Studio2.0 Beta 2版本号 老毕译注: ---------- canary channel: 金丝雀版本号,平均1到2周就会更新 ...

  5. [Scss Flex] Reuse Flexbox Styles With A Sass Mixin

    This lesson covers flexbox in a reusable mixin that should cover most layout situations on your site ...

  6. google analytics是什么(免费的网站流量分析服务:比如分析有多少个人来了你的网站,告诉你怎么样才能在网站上面实现最大收益。)

    google analytics是什么(免费的网站流量分析服务:比如分析有多少个人来了你的网站,告诉你怎么样才能在网站上面实现最大收益.) 一.总结 免费的网站流量分析服务:比如分析有多少个人来了你的 ...

  7. 借助gdb实现pstack

    pstack.sh: #! /bin/sh if [ -z $1 ] then echo "gdb script for print stack" echo "usage ...

  8. Linux系统下的单调时间函数

    欢迎转载,转载请注明出处:http://forever.blog.chinaunix.net 一.编写linux下应用程序的时候,有时候会用到高精度相对时间的概念,比如间隔100ms.那么应该使用哪个 ...

  9. Android shape画圆点

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  10. 【t070】二进制

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 求所有可以只用1和00拼成的长度为N的二进制数的个数除以15746的余数. 比如当N=4的时候,有5个 ...