C++设计范型:(到目前为止已经学习了两种C++设计范型)
(1)面向过程:按照面向过程式范型吧程序划分成不同的函数。
(2)面向对象:按照面向对象式范型把代码和数据组织成各种各样的类,并建立类之间的继承关系。
另一种范型:范型编程。
(3)泛型编程:泛型编程技术支持程序员创建函数和类的蓝图(即模板,template),而不是具体的函数和类。
PS:关键字class并不意味着这个是个类,这只是一种约定俗成的写法。在告诉计算机T是一种类型之后,就可以像对待一种普通数据类型那样使用他了。(写法是先写template<class T>接下来写要使用的函数模板的原型)
实现a和b两个地址的交换,也就是实现了传进来的两个参数值的交换。
例子:(函数模板)
#include<iostream>
#include<string>
template<class T>
void swap(T &a,T &b)
{
T tmp=a;//设置一个中间变量
a=b;
b=tmp;
}
int main()
{
int i1=3;
int i2=4;
std::cout<<“交换前i1=”<<i1<",i2="<<i2<<std::endl;
swap(i1,i2);//这种是传引用调用,我们传给他i1,i2,用一个&指针去引用他i1的地址
std::cout<<“交换后i1=”<<i1<",i2="<<i2<<std::endl;
std::string1 s1="我们";
std::string2 s2=“他们”;
std::cout<<“交换前s1=”<<s1<",s2="<<s2<<std::endl;
swap(s1,s2);//这种是传引用调用,我们传给他i1,i2,用一个&指针去引用他i1的地址
std::cout<<“交换后s1=”<<s1<",s2="<<s2<<std::endl;
return 0;
}
PS:
因为我们在调用类型过程中已经知道他要用什么类型的值了,如果是int整型就在<>尖括号里边写int,如果是double类型就在<>尖括号里边写double。但是如果你要写传统的方式也行,像普通函数调用两个尖括号<>不用写也是可以的。但是如果我们强调说我们调用的是一个函数模板我们可以这样子加上<>尖括号带一个数据类型。
---------类模板:(涉及到类的就是高级的东西)
具体的操作替换T,这里T只是一个占位符。
栈(stack):栈是限制仅在表的一端进行插入和删除运算的线性表,通常称插入和删除的一端为栈顶(top),另一端为栈底(bottom),当表中没有元素时称空栈。
例如我们每当调用一个函数和方法的时候,编译器就会使用一个栈来传递输入参数,首先他压入的是一个它的返回值,然后压入参数。
用类模板的方式写一个栈的程序:
#include<iostream>
#include<string>
template<class T>//定义一个类模板
class Stack
{
public:
Stack(unsigned int size = 1000);//构造函数(最好给他一个参数)
~Stack();//析构函数
void push(T value);//方法(是把一个value给推进栈)//value是作为push的一个参数,不需要把他作为一个成员
T pop();//(把栈里边的数据给推出来,不需要参数,但是需要返回值,不知道类型用T占位符)
private:
unsigned int size;//私有成员,表示栈的尺寸
unsigned int sp;//sp是栈指针(比如说我push进去了两个,要知道我现在push进去的元素在栈的第几个,要知道他的sp这个值指向的是哪一个,它指向b那我们就知道这个c是在b之上的,因为有这个指针才知道现在的状态)
T *data;//栈的数据(data存放栈的数据)
};
//下面是对类的实现
template<class T>
Stack<T>::Stack(unsigned int size)//实现构造器(传进去参数size如果没有值就默认为100)
{
this->size = size;//this->size就是类的成员中的size。后边的size就是传进来的参数size。用来表示我们这个栈有多大
data = new T(size);//对栈进行初始化。我们就用new指针,new一个T类型,我们不知道是什么类型,在实际的调用过程中才知道,我们在整个模板类的整个声明和定义中都用T占位符来表示一种类型,虽然不知道它的类型是什么,但是我们知道它的大小是size。
sp = 0;//sp默认是0
}
template<class T>
Stack<T>::~Stack()//析构器
{
delete[]data;//析构器就是删除内存,这里是删除数组(动态的删除数组)
}
template <class T>
void Stack<T>::push(T value)
{
data[sp++] = value;//0存放完之后再++
}
template<class T>
T Stack<T>::pop()//他的返回值是T,就是将当前sp指针,指向的这个栈的数据给弹回来(先--再sp)
{
return data[--sp];//data是一个数组
}
int main()
{
Stack<int> intStack(100);
intStack.push(1);
intStack.push(2);
intStack.push(3);//推123进去
std::cout << intStack.pop() << std::endl;
std::cout << intStack.pop() << std::endl;
std::cout << intStack.pop() << std::endl;//打印出来是321
return 0;
}
结果见上右图。
---------内联(inline)函数以及创建内联模板
有点类似于宏替换,把函数体替换到要调用处的函数名里边去。也就是说函数分为ABC三个函数,然后main函数来调用,如果说A是inline函数的话,那么他不通过普通调用函数的方式来调用,他会把整个A函数放到main函数调用它的这块地方,把它放到源代码里边去。
把上边的程序进行改写:(类的内联,我们自己给他写进去,不用加inline)
#include<iostream>
#include<string>
template<class T>//定义一个类模板
class Stack
{
public:
Stack(unsigned int size = 1000)//构造函数(最好给他一个参数)
{
this->size = size;
data = new T(size);
sp = 0;
}
~Stack()//析构函数
{
delete[]data;
}
void push(T value)
{
data[sp++] = value;//0存放完之后再++
}
T pop()
{
return data[--sp];//data是一个数组
}
private:
unsigned int size;
unsigned int sp;
T *data;
};
int main()
{
Stack<int> intStack(100);
intStack.push(1);
intStack.push(2);
intStack.push(3);//推123进去
std::cout << intStack.pop() << std::endl;
std::cout << intStack.pop() << std::endl;
std::cout << intStack.pop() << std::endl;//打印出来是321
return 0;
}
运行结果同上。(前边是按照传统的范式来写的,但是建议类的模板使用内联的方式,把定义过程直接写进来)
--------容器
上边的基于一个模板的stack类就是一个容器,它是一个栈容器。
例子:
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector < std::string > names;//把这个向量vector的名字叫做names
names.push_back("我");//往里边添加成员
names.push_back("你");
for (int i = 0; i < names.size(); i++)//知道他有多少个元素我们调用它的size()方法就可以了
{
std::cout << names[i] << std::endl;//可以使用像数组的方式来对它进行访问
}
return 0;
}
--------迭代器
使用迭代器完成相应的遍历:
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector < std::string > names;//把这个向量vector的名字叫做names
names.push_back("我");//往里边添加成员
names.push_back("你");
//每一个容器里边都会提供一个迭代器,迭代器的名字都一样iterator
//begin指向的是数据的起始位置,end是指向的是它最后一个元素的下一个位置,也就是他这个容器的底步
std::vector<std::string>::iterator iter = names.begin();//定义一个叫iter的迭代器,begin()就是他的开始位置(这里就是对申请的iter这个迭代器进行初始化,初始化我们调用了这个向量的begin方法,这个方法是返回他的开始位置的值)
//使用一个while循环
while (iter != names.end())//注意while的条件(names.end()是向量的最后一个元素的下一个位置,是他的底步(指向最下一个元素的底步(不是指向最下面的一个元素)))
{
std::cout << *iter << std::endl;//这个迭代器是一个智能指针,一个特殊的指针我们用*取他的值
++iter;//然后++指向下一个元素
}
return 0;
}
排序sort();函数
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
int main()
{
std::vector < std::string > names;//把这个向量vector的名字叫做names
names.push_back("I");
names.push_back("we");//往里边添加成员
names.push_back("you");
names.push_back("he");
names.push_back("she");
std::sort(names.begin(), names.end());//排序
std::vector<std::string>::iterator iter = names.begin();
while (iter != names.end())
{
std::cout << *iter << std::endl;
++iter;
}
return 0;
}