21天学C++(十九)模板

完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》

十九、模板

19.1 模板的定义和实现

#include <iostream>

const int DefaultSize = 10;

class Animal
{
public:
	Animal(int);
	Animal();
	~Animal() {}
	int GetWeight() const { return itsWeight; }
	void Display() const { std::cout << itsWeight; }
private:
	int itsWeight;
};

Animal::Animal(int weight):
itsWeight(weight)
{}

Animal::Animal():
itsWeight(0)
{}

template <class T> //模板声明
class Array{
public:
	Array(int itsSize = DefaultSize); //构造函数
	Array(const Array &rhs);   //复制构造函数
	~Array() { delete [] pType; }   //析构函数

	Array& operator=(const Array&);  //赋值函数
	T& operator[](int offSet) { return pType[offSet]; }  //取值
	const T& operator[](int offSet) const
	{ return pType[offSet]; }
	int GetSize() const { return itsSize; }

private:
	T *pType;
	int  itsSize;
};

template <class T>
Array<T>::Array(int size):
itsSize(size)
{
	pType = new T[size];
	// the constructors of the type you are creating
	// should set a default value
}

template <class T>
Array<T>::Array(const Array &rhs)
{
	itsSize = rhs.GetSize();
	pType = new T[itsSize];
	for (int i = 0; i<itsSize; i++)
		pType[i] = rhs[i];
}

template <class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
	if (this == &rhs)
		return *this;
	delete [] pType;
	itsSize = rhs.GetSize();
	pType = new T[itsSize];
	for (int i = 0; i<itsSize; i++)
		pType[i] = rhs[i];
	return *this;
}

int main(){
	Array<int> theArray;      // 整型数组
	Array<Animal> theZoo;     // 类型数组
	Animal *pAnimal;
	for (int i = 0; i < theArray.GetSize(); i++)	{
		theArray[i] = i*2;
		pAnimal = new Animal(i*3);
		theZoo[i] = *pAnimal;
		delete pAnimal;
	}
	for (int j = 0; j < theArray.GetSize(); j++)	{
		std::cout << "theArray[" << j << "]:\t";
		std::cout << theArray[j] << "\t\t";
		std::cout << "theZoo[" << j << "]:\t";
		theZoo[j].Display();
		std::cout << std::endl;
	}
	return 0;
}

执行结果如下:

theArray[0]:    0               theZoo[0]:      0
theArray[1]:    2               theZoo[1]:      3
theArray[2]:    4               theZoo[2]:      6
theArray[3]:    6               theZoo[3]:      9
theArray[4]:    8               theZoo[4]:      12
theArray[5]:    10              theZoo[5]:      15
theArray[6]:    12              theZoo[6]:      18
theArray[7]:    14              theZoo[7]:      21
theArray[8]:    16              theZoo[8]:      24
theArray[9]:    18              theZoo[9]:      27
请按任意键继续. . .

19.2 模板函数

#include <iostream>
using namespace std;

template<class T>
void swap(T left, T right){
	cout<<left<<"\t"<<right<<endl;
	T temp;
	temp = left;
	left = right;
	right = temp;
	cout<<left<<"\t"<<right<<endl;
}
int main(){
	swap(1, 2);
	swap(1.1, 2.2);
	return 0;
}

执行结果如下:

1       2
2       1
1.1     2.2
2.2     1.1
请按任意键继续. . .

19.3 C++ STL

19.3.1 标准模板库 STL

一个基于模板的容器类库,包括链表、队列、列表和堆栈,还包括许多常用的算法,如排序、查找等;

19.3.2 容器

容器是包容其他对象的对象;

STL容器类有两种类型:顺序和关联

  • 顺序容器:提供对其他成员成员或元素的顺序和随机访问;
  • 关联容器:是经过优化按关键值访问它们的元素;

19.3.3 常用顺序容器:vector

类似数组,提供经过优化来对其元素按下标进行快速访问的容器;

容器可必要时自我增长,自我扩容;

其内部元素用new()创建,用运算符delete()释放,即用户须为自己的类提供显示默认构造函数;

  • 如:vector<int>  vInts;
  • 如: vector<flost> vFloats;
  • 如:vector<Student> vStudents(50);创建能容纳50个对象的容器,访为则用vStudent[0]等下标访问;
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include <vector>
class Student{
public:
    Student(); //显示定义构造函数
    Student(const string& name, const int age); //重载构造函数
    Student(const Student& rhs);  //复制构造函数
    ~Student();    //析构函数

    void   SetName(const string& name);
    string   GetName()   const;
    void   SetAge(const int age);
    int      GetAge()   const;

    Student& operator=(const Student& rhs);  //赋值函数
    friend ostream& operator<<(ostream& os, const Student& rhs); //输出
};
Student::Student()
: itsName("New Student"), itsAge(16)
{}

Student::Student(const string& name, const int age)
: itsName(name), itsAge(age)
{}

Student::Student(const Student& rhs)
: itsName(rhs.GetName()), itsAge(rhs.GetAge())
{}

Student::~Student()
{}

void Student::SetName(const string& name)
{
	itsName = name;
}

string Student::GetName() const
{
	return itsName;
}

void Student::SetAge(const int age)
{
	itsAge = age;
}

int Student::GetAge() const
{
	return itsAge;
}

Student& Student::operator=(const Student& rhs)
{
	itsName = rhs.GetName();
	itsAge = rhs.GetAge();
	return *this;
}

ostream& operator<<(ostream& os, const Student& rhs)
{
	os << rhs.GetName() << " is " << rhs.GetAge() << " years old";
	return os;
}

template<class T>
void ShowVector(const vector<T>& v);    

typedef vector<Student>   SchoolClass;

int main(){
	Student Harry;
	Student Sally("Sally", 15);
	Student Bill("Bill", 17);
	Student Peter("Peter", 16);

	SchoolClass   EmptyClass;
	cout << "EmptyClass:" << endl;
	ShowVector(EmptyClass);//输出函数

	SchoolClass GrowingClass(3);//构造函数
	cout << "GrowingClass(3):" << endl;
	ShowVector(GrowingClass);

	GrowingClass[0] = Harry;
	GrowingClass[1] = Sally;
	GrowingClass[2] = Bill;
	cout << "GrowingClass(3) after assigning students:" << endl;
	ShowVector(GrowingClass);//赋值函数

	GrowingClass.push_back(Peter);//复制构造函数
	cout << "GrowingClass() after added 4th student:" << endl;
	ShowVector(GrowingClass);

	GrowingClass[0].SetName("Harry");
	GrowingClass[0].SetAge(18);
	cout << "GrowingClass() after Set\n:";
	ShowVector(GrowingClass);

	return 0;
}

template<class T> void ShowVector(const vector<T>& v){//模板参数函数
    cout << “max_size() = ” << v.max_size();//容器中最大元素数目
    cout << "\tsize() = " << v.size(); //
    cout << “\tcapacity() = ” << v.capacity(); //编译器内分配容器,倍数增加
    cout << "\t" << (v.empty()? "empty": "not empty");
    cout << endl;

    for (int i = 0; i < v.size(); ++i)
        cout << v[i] << endl; //调用输出函数,直接输出类对象
}

执行结果如下:

EmptyClass:
max_size() = 119304647  size() = 0      capacity() = 0  empty

GrowingClass(3):
max_size() = 119304647  size() = 3      capacity() = 3  not empty
New Student is 16 years old
New Student is 16 years old
New Student is 16 years old

GrowingClass(3) after assigning students:
max_size() = 119304647  size() = 3      capacity() = 3  not empty
New Student is 16 years old
Sally is 15 years old
Bill is 17 years old

GrowingClass() after added 4th student:
max_size() = 119304647  size() = 4      capacity() = 4  not empty
New Student is 16 years old
Sally is 15 years old
Bill is 17 years old
Peter is 16 years old

GrowingClass() after Set
:max_size() = 119304647 size() = 4      capacity() = 4  not empty
Harry is 18 years old
Sally is 15 years old
Bill is 17 years old
Peter is 16 years old

请按任意键继续. . .

19.3.4 常用顺序容器:如list

list用于提供对元素的频繁插入或删除进行优化的容器;

list是用双向链表实现的,其中每个节点即和前一个节点链接又和下一个节点链接;

链接通常是指针实现的,标准列表容器类使用一个叫多重指针iterator的方法;

  • 成员函数begin返回指向列表中的第一个节点的多重指针;
  • 成员函数end返回最后一个即诶但的后面一个节点的多种指针
  • 成员函数push_front、pop_front、push_back、pop_back;
#include <iostream>
#include <list>
using namespace std;

typedef list<int> IntegerList;

int main()
{
	IntegerList   intList;

	for (int i = 1; i <= 10; ++i)
		intList.push_back(i * 2);

	for (IntegerList::const_iterator ci = intList.begin();
		ci != intList.end(); ++ci)
		cout << *ci << " ";

	return 0;
}

执行结果如下:

2 4 6 8 10 12 14 16 18 20 请按任意键继续. . .

19.3.5 堆栈:如stack:后进先出

  • 堆栈是一个可以在尾端增长和收缩的连续分配区域;
  • 堆栈中的元素只能从后面访问或删除;
  • 可以容纳任何元素,但前提是所有元素必须是同一类型;

19.3.6 队列:如stack:先进先出

队列是在一端增加,另一端取出;

19.3.7 关联容器

可以对元素进行快速随机访问,包括映射、多重映射、集合、多重集合;

  • 映射:键值唯一,(key, value);其中key唯一;
  • 多重映射:没有唯一限制的映射类,多个元素可以有相同的键值;
  • 集合:和映射类似,差别是元素不是(key, value),一个元素就是唯一键;
  • 多重集合:允许双重键值的集合类;
#include <iostream>
#include <string>
#include <map>
using namespace std;

class Student
{
public:
	Student();
	Student(const string& name, const int age);
	Student(const Student& rhs);
	~Student();

	void   SetName(const string& name);
	string   GetName()   const;
	void   SetAge(const int age);
	int      GetAge()   const;

	Student& operator=(const Student& rhs);

private:
	string itsName;
	int itsAge;
};

Student::Student()
: itsName("New Student"), itsAge(16)
{}

Student::Student(const string& name, const int age)
: itsName(name), itsAge(age)
{}

Student::Student(const Student& rhs)
: itsName(rhs.GetName()), itsAge(rhs.GetAge())
{}

Student::~Student()
{}

void Student::SetName(const string& name)
{
	itsName = name;
}

string Student::GetName() const
{
	return itsName;
}

void Student::SetAge(const int age)
{
	itsAge = age;
}

int Student::GetAge() const
{
	return itsAge;
}

Student& Student::operator=(const Student& rhs)
{
	itsName = rhs.GetName();
	itsAge = rhs.GetAge();
	return *this;
}

ostream& operator<<(ostream& os, const Student& rhs)
{
	os << rhs.GetName() << " is " << rhs.GetAge() << " years old";
	return os;
}


template<class T, class A>
void ShowMap(const map<T, A>& v);    // display map properties

typedef map<string, Student>   SchoolClass;

int main()
{
	Student Harry("Harry", 18);
	Student Sally("Sally", 15);
	Student Bill("Bill", 17);
	Student Peter("Peter", 16);

	SchoolClass   MathClass;
	MathClass[Harry.GetName()] = Harry;
	MathClass[Sally.GetName()] = Sally;
	MathClass[Bill.GetName()] = Bill;
	MathClass[Peter.GetName()] = Peter;

	cout << "MathClass:" << endl;
	ShowMap(MathClass);

	cout << "We know that " << MathClass["Bill"].GetName()
		<< " is " << MathClass["Bill"].GetAge() 
		<< " years old" << endl;
	return 0;
}

template<class T, class A>
void ShowMap(const map<T, A>& v)
{
	for (map<T, A>::const_iterator ci = v.begin();
		ci != v.end(); ++ci)
		cout << ci->first << ": " << ci->second << endl;

	cout << endl;
}

执行结果如下:

MathClass:
Bill: Bill is 17 years old
Harry: Harry is 18 years old
Peter: Peter is 16 years old
Sally: Sally is 15 years old

We know that Bill is 17 years old
请按任意键继续. . .

19.4 算法类

标准库提供了大约60中标准算法来执行最基本、最常见的容器运算:如for_each算法

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

template<class T>
class Print
{
public:
	void operator()(const T& t)
	{
		cout << t << " ";
	}
};

int main()
{
	Print<int>   DoPrint;
	vector<int>   vInt(5);

	for (int i = 0; i < 5; ++i)
		vInt[i] = i * 3;

	cout << "for_each()" << endl;
	for_each(vInt.begin(), vInt.end(), DoPrint);
	cout << endl;

	return 0;
}

执行结果如下:

for_each()
0 3 6 9 12
请按任意键继续. . .
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

template<class T>
class Print
{
public:
	void operator()(const T& t)
	{
		cout << t << " ";
	}
};

int main()
{
	Print<int>   DoPrint;
	vector<int>  vInt(10);

	fill(vInt.begin(), vInt.begin() + 5, 1);
	fill(vInt.begin() + 5, vInt.end(), 2);

	for_each(vInt.begin(), vInt.end(), DoPrint);
	cout << endl << endl;

	return 0;
}

执行结果如下:

1 1 1 1 1 2 2 2 2 2

请按任意键继续. . .

发表回复