泛型编程:
1:STL目前在国内的资料比较少,国外相对较多。STL用得好的话,一个人的效率相当于十个人。
1:编程就是生产一个工具,这个工具他能够帮人们做很多重复的事情!
2:编程过程中,我们需要进行设计,分析,更新,维护。我们需要考虑的是这个软件的架构,代码量等。
3:编码量将决定我们需要多少人来工作,所以当时Java很流行,生产力大。C++也推出了模版编程。
4:模版它是一个能够产生代码的代码。这也是模版的意义。他改变了很多设计方式。
5:他从设计角度帮我们解决很多问题。
2:牲口圈舍的设计:
按C语言的思维(这个图是C++,但思想是C思想),如果再加一只羊,我们就得加两个模块(羊和羊圈),他们都是强聚合关系,最后将非常难以管理。
按方案二:将所有动物抽象成一个父类,这样接口很简单,谁进去,就变成什么棚,适合哪种动物。这就是多态的机制实现的自动匹配。但是,这样和现实是脱节的,不可能有这样一个东西,虽然代码量小,但是难以让人理解。
根据方案三:泛型编程(自动推导式编程),他抽象出来的是实际有的。相当于一个产生器,会产生实实在在存在的代码。
编程的思想:
1:泛型编程,面相对对象,使用对象,他和我们语言的本身是没有关系的,他们都是一种思想、是一种哲学。
2:上面的例子就是三种思想:过程、对象、泛型。三种思想最终的目的都是写出好的程序,他们本身是没有好坏之分的。
类模版基础使用:
1:之前我们自己写的Array类只能保存一种类型的数据,但是STL里面的却可以保存任何类型的。我们可以使用模版的方式来实现。
2:template<typename T>;。typename后面跟的是一个“类型变量”,之后传什么类型,这个T就代表什么类型。
3:template<class T>;。template后面只可能跟这两个关键字,他们的作用都是一样的。但是class使用得比较少,已被废弃,但编译器还可以使用。
4:测试代码:
#ifndef ARRAY_H_
#define ARRAY_H_
template<typename T>//已经开始在做泛型了。
class Array
{
public:
explicit Array(const size_t size = 10);
~Array();
explicit Array(const Array& other);
Array& operator=(Array&other);
const T& operator[](const size_t index) const;
T& operator[](const size_t index);
void Append(const T value);
private:
T *data_;
size_t size_;
unsigned int pos_;
};
//--------------------------------------函数实现-----
template<typename T>
void Array<T>::Append(const T value)
{
data_[pos_++] = value;
}
template<typename T>
T& Array<T>::operator[](const size_t index)
{
return data_[index];
}
template<typename T>
const T& Array<T>::operator[](const size_t index) const
{
return data_[index];
}
template<typename T>
Array<T>& Array<T>::operator=(Array<T>&other)
{
if (&other == this)
return *this;
delete[]data_;
size_ = other.size_;
data_ = new int[size_];
memcpy(data_, other.data_, size_*sizeof(T));
return *this;
}
template<typename T>
Array<T>::Array(const Array<T>& other)
{
data_ = new T[other.size_];
size_ = other.size_;
memcpy(data_, other.data_, size_*sizeof(T));
}
template<typename T>
Array<T>::~Array()
{
delete[]data_;
}
template<typename T>
Array<T>::Array(const size_t size /*= 10*/)
{
data_ = new T[size_];
}
#endif//!ARRAY_H_
//main.c类容:
#include <iostream>
#include "Array.h"
int main()
{
// int ar[10] = { 0 };
Array<int> a(10);
//Array只是一个int型的Array。如果要保存double类型的数据,就又得做一个保存double的类。
//这时候就可以用泛型的方式来解决。
a.Append(1);
a.Append(2);
a.Append(3);
a.Append(4);
a.Append(5);
//宏是编译时替换,而模版是编译时生成。编译器在编译的时候会copy生成多个类,它对应你是用过的类型。
Array<char> c;
c.Append('a');
c.Append('b');
c.Append('c');
c.Append('d');
c.Append('e');
std::cout << a[3] << std::endl;
std::cout << c[3] << std::endl;
return 0;
}
类外部模版类的类名:
使用模版后,class的类型已经发生了改变的,应该在类名后面加上<T>.
函数模版:
在类里面使用的模版成为类模版,这个类称为模版类。
#include <iostream>
template<typename T, typename T2>
class Demo
{
public:
Demo(T2 in)
{
}
private:
T tdemo;
T2 t2Demo;
};
template<typename T, int size>//模版参数,可以直接拿来使用,但必须是常量。
class Demo3
{
public:
Demo3(T in)
{
size_ = size;
}
private:
int size_;
T tdemo;
};
int main()
{
Demo <int, float> demo(float(1.003));
return 0;
}
1:模版还可以和函数相结合使用,最著名的就是Swap交换函数。它可以推导参数的类型,产生相应的代码。
2:你使用了多少类型,他就会生成多少个函数,而且每个函数的地址是不一样的。
3:如果我们使用的方式出错,他就会推到不出来,导致出错,这时候,我们可以强制指定类型。
4:模版函数是可以重载的。也可以特化(把将会生成的函数自己写一遍,实现特殊的效果)。
5:但凡使用了模版,就很可能存在重载,所以可能存在二义性的问题。而且在模版里面的重载是不可以进行提升转换的。
#include <iostream>
template<typename T, typename T2>
class Demo
{
public:
Demo(T2 in)
{
}
private:
T tdemo;
T2 t2Demo;
};
template<typename T, int size>//模版参数,可以直接拿来使用,但必须是常量。
class Demo3
{
public:
Demo3(T in)
{
size_ = size;
}
private:
int size_;
T tdemo;
};
template<typename T>
void Swap(T&lhs, T&rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
void Swap(int&lhs, double&rhs)//重载的
{
}
void Swap(int&lhs, int&rhs)//特化的(相当于一个特例,模版不再自己生成这个)
{
}
template<typename T1, typename T2>
void Swap(T1 lhs, T2 rhs)
{
Swap<double>(lhs, rhs);
}
int main()
{
Demo <int, float> demo(float(1.003));
int a = 10, b = 20;
Swap(a, b);
std::cout << a << " " << b << std::endl;
int aa = 10;
double bb = 20;
// Swap(aa, bb);//这行代码会出错。
// Swap<double>(aa, bb);//会强制生成double的版本。
//error C2664: “void Swap<double>(T &,T &)”: 无法将参数 1 从“int”转换为“double &”
Swap(aa, bb);//将会调用重载的函数。
return 0;
}