类模版与函数模版的区别
1.类模版使用只能用显式指定类型的方式
2.类模版中的模版参数列表可以有默认参数
#include<iostream>
using namespace std;
//template<class NameType,class AgeType>
template<class NameType, class AgeType = int>//类模版的模版参数列表里可以有默认参数
class Person {
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_age = age;
}
void showPerson() {
cout << "name= " << this->m_Name <<"\t" << "age= " << this->m_age << endl;
}
public:
NameType m_Name;
AgeType m_age;
};
void test01() {
//Person p1("孙悟空", 999); //错误,类模版无法进行自动类型推导
Person<string, int>p1("孙悟空", 999); //类模版只能使用显式指定类型方式
p1.showPerson();
}
void test02() {
Person<string>p2("猪八戒", 1);//当类模版的模版参数列表里有默认参数时,显示指定类型指定剩下那一个即可;
p2.showPerson();
}
int main() {
test01();
test02();
}
类模版中的成员函数创建时机
#include<iostream>
using namespace std;
//首先创建2个类
class Person1{
public:
void showPerson1() {
cout << "Person1的调用" << endl;
}
};
class Person2 {
public:
void showPerson2() {
cout << "Person2的调用" << endl;
}
};
//创建一个类模版,类模版一致的数据类型为T;模版必须要确定T的数据类型,才可以使用;
template<class T>
class MyClass
{
public:
T obj;
void func1()
{
obj.showPerson1();
}
void func2()
{
obj.showPerson2();
}
};
void test01() {
MyClass<Person1>m;
m.func1();
}
int main() {
test01();
}
类模版对象做函数参数
通过类模版创建的对象,可以有三种方式向函数中进行传参,使用比较广泛的是第一种。
1.指定传入类型----直接显示对象的数据类型
2.参数模板化-------将对象中的参数变为模版进行传递
3.整个类模版化----将这个对象模板化进行传递
#include<iostream>
using namespace std;
#include<string>
template<class NameType, class AgeType = int>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
//1、指定传入的类型
void printPerson1(Person<string, int>& p)
{
p.showPerson();
}
void test01()
{
Person <string, int >p("孙悟空", 100);
printPerson1(p);
}
//2、参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
p.showPerson();
cout << "T1的类型为: " << typeid(T1).name() << endl;
cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
Person <string, int >p("猪八戒", 90);
printPerson2(p);
}
//3、整个类模板化
template<class T>
void printPerson3(T& p)
{
cout << "T的类型为: " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Person <string, int >p("唐僧", 30);
printPerson3(p);
}
int main()
{
test01();
test02();
test03();
}
类模版与继承
当类模板碰到继承时,需要注意一下几点:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T的类型,子类也需变为类模板
#include<iostream>
using namespace std;
#include<string>
template<class T>
class Base {
public:
T m;
};
class Son :public Base<int>//当子类继承的父类是一个类模版时,子类在声明的时候要指定父类中T的类型;
{
};
template<class T1,class T2> //也可以用T2指定父类中的T类型
class Son2 : public Base<T2>
{
public:
Son2()
{
cout << "T1的类型是" << typeid(T1).name<< endl;
cout << "T2的类型是 " << typeid(T2).name << endl;
}
};
void test01() {
Son s1;
}
void test02() {
Son2<int,char>child1;
}
int main() {
test01();
test02();
}
类模版成员函数类外实现
类模版中成员函数类外实现时,需要加上模版参数列表
#include<iostream>
using namespace std;
#include<string>
template<class T1,class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template<class T1, class T2>
Person<T1,T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数类外实现
template<class T1, class T2>
void Person<T1,T2>::showPerson() {
cout << "姓名" << this->m_Name << "\t" << "年龄" << this->m_Age << endl;
}
void test01() {
Person<string, int>p("Tom", 20);
p.showPerson();
}
int main() {
test01();
}
类模版分文件编写
问题:类模版中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决方法:
1、直接包含cpp源文件
2、将声明和实现写到同一个源文件中,并更改后缀名是.hpp,hpp是约定的名称,并不是强制
主流解决方式是第二种
person.hpp中代码:
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T1, class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
类模板分文件编写.cpp中代码
#include<iostream>
using namespace std;
将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test01()
{
Person<string, int> p("Tom", 10);
p.showPerson();
}
int main() {
test01();
}
类模版与友元
掌握类模版配合友元函数的类内类外实现
全局函数类内实现----直接在类内声明友元即可
全局函数类外实现----需要提前让编译器知道全局函数的存在
全局函数类内实现----直接在类内声明友元即可
#include<iostream>
using namespace std;
#include<string>
template<class T1,class T2>
class Person {
//全局函数类内实现
friend void printPerson(Person<T1, T2>& p)
{
cout << "名字是" << p.m_Name << "\t" << "年龄是" << p.m_Age << endl;
}
public:
Person(T1 name, T2 age){
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test01() {
Person<string,int> p1("Tom",20);
printPerson(p1);//调用全局函数直接写函数名即可;
}
int main() {
test01();
}
//全局函数的类外实现
#include<iostream>
using namespace std;
#include<string>
template<class T1,class T2>
class Person {
friend void printPerson2<>(Person<T1, T2>& p); //加一个空模版的参数列表即可
public:
Person(T1 name, T2 age){
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
//全局函数的类外实现
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p) {
cout << "名字是" << p.m_Name << "\t" << "年龄是" << p.m_Age << endl;
}
void test02() {
Person<string,int> p2("Jerry",30);
printPerson2(p2);
}
int main() {
test02();
}
类模版案例–数组类封装的需求分析
class MyArray
{
private:
//数组 T * pAddress;
//容量 a_Capacity;
//大小 m_Size;
public:
构造函数(容量)
拷贝构造
operator =
利用下标的方式访问数组中的元素 ?
尾插法
尾删法
获取数组容量
获取数组大小
析构
}
类模版案例–数组类封装(上)
类模版案例–数组类封装(下)
STL的诞生
1、c++面向对象的思想是封装、继承、多态;泛型编程思想是类模版和函数模版
封装就是把它的属性和行为抽象出来,作为一个整体,来实现这些事和物,提高了复用性;//封装简单理解为物以类聚;
继承就是子类继承父类,子类把父类的属性和行为继承下来,把它的属性和行为拿到手,不用再重新声明,提高了复用性;
多态就是一个函数名称可以有多个接口,由于对象不同,父类指针指向子类对象不同,对象创建的不同调用同一个接口会产生不同的形态;也是提高了复用性;
2、在大多数情况下,数据结构和算法都没有一套标准,导致被迫从事大量重复工作。
3、为了建立数据结构和算法的一套标准,诞生了STL
STL的基本概念
1、STL(standarad Template Library:标准模版库)
2、STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
3、容器和算法之间通过迭代器进行无缝衔接
4、STL几乎所有的代码都采用了模版类或者模版函数
STL六大组件
STL大体分为六大组件,分别是:容器,算法,迭代器,仿函数,适配器(配接器),空间配置器
1、容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据
2、算法:各种常用的算法,如sort、find、copy、for_each等
3、迭代器:扮演了容器与算法之间的胶合剂
4、仿函数:行为类似函数,可作为算法的某种策略
5、适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
6、空间配置器:负责空间的配置与管理
STL中容器、算法、迭代器
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组、链表、树、栈、队列、集合、映射表等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科称为算法。
算法分为质变算法和非质变算法
质变算法:是指运算过程中会更改区间内元素的内容,例如拷贝、替换、删除等等
非质变算法:是指运算中不会更改区间中的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器。
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针。