两种C++程序设计范型,即:
1、按照面向过程式范型把程序划分成不同的函数。
2、按照面向对象式范型把代码和数据组织成各种各样的类并建立类之间的继承关系。
泛型编程
泛型编程技术支持程序员创建函数和类的蓝图(即模板,template),而不是具体的函数和类。
这些模板可以没有任何类型:它们可以处理的数据并不仅限于某种特定的数据类型。
标准模板库(Standard Template Library, STL)
在泛型编程技术里,我们仍然需要编写自己的函数和类,但不必限定它们所使用的数据类型。
只需要使用一个占位符(通常用字母T来表示)然后用这个占位符来编写函数。
当程序需要这段代码时,你提供数据类型,编译器将根据你的模板即时生成实用的代码。
foo()的函数模板:
template <class T>
void foo(T param)
{
// do something
}
注意:
第一行代码里,在尖括号里有一个class T,用来告诉编译器:字母T将在接下来的函数里代表一种不确定的数据类型。关键字class并不意味着这个是类哦,这只是一种约定俗成的写法。
在告诉计算机T是一种类型之后,就可以像对待一种普通数据类型那样使用它了。
函数模板
交换两个变量的值:
void swap(int &a, int &b){
int tmp = a;
a = b;
b = tmp;
}
注意:
在创建模板时,还可以用template <typename T>来代替template <class T>,它们的含义是一样一样的。
注意,template <class T>中的class并不意味着T只能是一个类。
再强调一次,不要把函数模板分成原型和实现两个部分。
为了明确地表明swap()是一个函数模板,还可以使用swap<int>(i1, i2)语法来调用这个函数,这将明确地告诉编译器它应该使用哪一种类型。
如果某个函数对所有数据类型都将进行同样的处理,就应该把它编写为一个模板。
如果某个函数对不同的数据类型将进行不同的处理,就应该对它进行重载。
类模板
类模板与函数模板非常相似:同样是先由你编写一个类的模板,再由编译器在你第一次使用这个模板时生成实际代码。
template <class T>
class MyClass{
MyClass();
void swap(T &a, T &b);
}
构造器的实现将是下面这样:
MyClass<T>::MyClass(){
// 初始化操作。
}
因为MyClass是一个类模板,所以不能只写出MyClass::MyClass(),编译器需要你在这里给出一种与MyClass()配合使用的数据类型,必须在尖括号里提供它。
因为没有确定的数据类型可以提供,所以使用一个T作为占位符即可。
栈是实际编程过程中一种非常有用的数据结构,它是一种数据存储机制。
栈只提供两个函数:一个用来把数据压入栈的顶部,另一个用来从栈取出顶部元素(先进后出)
<span style="font-family:KaiTi_GB2312;font-size:18px;">#include "stdafx.h" #include <iostream> #include<string> template<class T> class Stack//函数模板的原型和定义要写在一起 { public: Stack(unsigned int size = 100); ~Stack(); void push(T value); T pop(); private: unsigned int size; unsigned int sp; T *data; }; template<class T> Stack<T>::Stack(unsigned int size) { this->size = size; data = new T[size]; sp = 0;//开始时指向栈底 } template<class T> Stack<T>::~Stack() { delete[]data; } template<class T> void Stack<T>::push(T value) { data[sp++] = value; } template<class T> T Stack<T>::pop() { return data[--sp]; } int main() { Stack<int> intStack(100); intStack.push(1); intStack.push(2); intStack.push(3); std::cout << intStack.pop() << std::endl; std::cout << intStack.pop() << std::endl; std::cout << intStack.pop() << std::endl; return 0; }</span>
内联模板(即将类的声明和定义放在一起)
inline int add (int x, int y, int z){
return x+y+z;}
在程序中,调用其函数时,该函数在编译时被替代,而不像一般函数那样是在运行时被调用。
在创建类模板时,避免类声明和类定义相分离的一个好办法是使用内联方法。
在类里,内联方法的基本含义是在声明该方法的同时还对它进行定义。(即类的声明和定义写在了一起)
class Person{
Person(std::string name)
{
this -> name = name;
}
// … …}
上例变成了:
<span style="font-family:KaiTi_GB2312;font-size:18px;">#include "stdafx.h" #include <iostream> #include"stdlib.h" #include <stdio.h> #include<vector> #include<string> #include "header.h" template<class T> class Stack//函数模板的原型和定义要写在一起 { public: Stack(unsigned int size = 100) { this->size = size; data = new T[size]; sp = 0;//开始时指向栈底 } ~Stack() { delete[]data; } void push(T value) { data[sp++] = value; } T pop() { return data[--sp]; } private: unsigned int size; unsigned int sp; T *data; }; int main() { Stack<int> intStack(100); intStack.push(1); intStack.push(2); intStack.push(3); std::cout << intStack.pop() << std::endl; std::cout << intStack.pop() << std::endl; std::cout << intStack.pop() << std::endl; return 0; }</span>
除了可以更好地帮助编译器处理类模板之外,使用内联方法还有一个很好的作用:可以让你少打些字并让源代码的可读性变得更好。
建议:在使用类模板的时候,我们直接将函数的声明和定义写在一起,即在类声明的时候顺便将它进行定义。
如果你打算在自己的程序里使用Stack模板,一定要给它增加一个副本构造器和一个赋值操作符重载。
我们刚刚的代码还缺少必要的错误处理功能,例如在栈已满的时候调用push()方法,或者在栈为空的时候调用pop()方法,会导致程序运行出错。
我们应该设法让栈在遇到这两种情况的时候抛出一个异常来处理。
C++并没有限制只能使用一个类型占位符,如果类模板需要一种以上的类型,根据具体情况多使用几个占位符即可。
template <class T, class U>
class MyClass{
// … …
}
在实例化时,我们只需要这么做:
MyClass<int, float> myClass;