你的c++学习路上明灯_哔哩哔哩_bilibili 上述是我在B站发布的相应视频
目录
5.模板的局限性:模板的通用性并不是万能的(在某些特定的时候是不合适的)
5.类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
这几天会更的比较快,因为要做的事情比较多,我觉得,如果我选择了一些东西我就要为之努力,就像我在CSDN上分享我的学习经历,那么我就要保证自己的学习质量和速度,包括自己学习的量,质量和数量都是要的,如果有什么不足之处,真的希望xdm不吝赐教。
好了,结束了我们c++核心课程,进入到c++的提高课程,模板和STL部分,为什么先讲模板呢?因为我先学的模板,老师先教的模板,哈哈哈哈,废话文学功底见长,其实这本身就是有一个顺序的,哈哈哈。不皮了,我们开始下面的学习。
模板:建立通用的模具,提高复用性
模板的目的主要就是:提高复用性,将类型参数化。
说人话就是,懒得写那么多,一个就够用了。
拿个通用的例子来讲,都写过英语作文吧,都背过作文模板吧,不管要写多少作文,反正能用这个模板就直接套,都不用背其他的模板了;
一,基本介绍
1.特点:
1)不能直接使用(只是一个框架)‘
2)模板的通用不是万能的;
2.c++提供两种模板机制:
1)函数模板:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来表示。
2)类模板:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来表示。
二,详细的使用
一,函数模板
1.函数模板的基本语法
1)函数模板利用关键字template
2)使用函数模板有两种方式,1.自动类型推导。
2,显示指定类型·
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//交换两个整型的函数
void SwapInt(int &a,int &b) {
int temp = a;
a = b;
b = temp;
}
//交换两个double类型的函数
void SwapDouble(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
//用函数模板来写
//提高该段代码的复用性
template<typename T>
void MySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test1() {
int a = 10;
int b = 20;
SwapInt(a, b);
cout << "a = " << a << endl << "b = " << b << endl;
double c = 3.13;
double d = 2.11;
SwapDouble(c, d);
cout << "c = " << c << endl << "d = " << d << endl;
//1.编译器自动类型推导
MySwap(a, b);
cout << "a = " << a << endl << "b = " << b << endl;
//2.显示指定类型
MySwap<int>(a, b);
cout << "a = " << a << endl << "b = " << b << endl;
}
int main() {
test1();
return 0;
}
2.函数模板的注意事项:
1)自动类型推导,必须推导出一致的数据类型T,才可以使用。
2)模板必须要确定出T的数据类型,才可以使用。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T>
void MySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test1() {
int a = 10;
int b = 20;
double c = 3.24;
//只能转换相同数据类型的
//MySwap(a, b);//正确
//MySwap(a, c);//错误
}
template<class T>
void func() {
cout << "func的调用" << endl;
}
void test2() {
//func(); 因为func函数中并没有用到数据,所以无法确定T的数据类型
//故,无法调用func函数
//如果想调用该函数,就必须确定T的数据类型,任何数据类型都行,反正也不用T
func<int>();
func<double>();
}
int main() {
test1();
test2();
return 0;
}
3.普通函数与函数模板的区别:
1)普通函数调用时,可以发生自动类型转换(隐式类型转换)
2)函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换。
如果利用显示指定类型的方式,可以发生隐式类型转换。(推荐使用)
4.普通函数和函数模板的调用规则:
1)如果函数模板和普通函数都能实现,优先调用普通函数,
2)可以通过空模板参数列表来强制使用函数模板
3)函数模板也可以发生重载
4)如果函数模板可以产生更好的匹配,优先调用函数模板
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
void MyPrint(int a,int b) {
cout << "普通函数" << endl;
}
template<class T>
void MyPrint(T a, T b) {
cout << "函数模板" << endl;
}
//函数模板也可以发生重载
template<class T>
void MyPrint(T a, T b,T c) {
cout << "函数模板重载" << endl;
}
void test1() {
int a = 10;
int b = 20;
//如果函数模板和普通函数都能实现,优先调用普通函数
MyPrint(a, b);
//空模板参数列表强制调用函数模板
MyPrint<>(a , b);
char c = 'c';
char d = 'd';
//如果调用普通函数,则需要隐式转换,
//如果函数模板可以产生更好的匹配,优先调用函数模板
MyPrint(c, d);
}
int main() {
test1();
return 0;
}
5.模板的局限性:模板的通用性并不是万能的(在某些特定的时候是不合适的)
c++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
1)利用具体化的模板,可以解决自定义类型的通用化
2)学习模板并不是为了写模板,而是在STL中能够运用系统提供的模板
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string>
class person {
public:
person(string name,int age) {
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
template<class T>
bool Compare(T& a, T& b) {
if (a == b)
return true;
return false;
}
//利用具体化person的版本实现代码,具体化优先调用于普通模板
//具体化:显示具体化的原型。以template<>开头
template<> bool Compare(person& p1, person& p2) {
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) {
return true;
}
else
return false;
}
void test1() {
int a = 10;
int b = 20;
int ret = Compare(a, b);
if (ret) {
cout << "a和b相等" << endl;
}
else
cout << "a和b不相等" << endl;
person p1("Tom",18);
person p2("Tom", 18);
int r = Compare(p1, p2);
if (r) {
cout << "p1和p2相等" << endl;
}
else
cout << "p1和p2不相等" << endl;
}
int main() {
test1();
return 0;
}
二,类模板
1.类模板和函数模板的区别:
1)类模板没有自动类型推导的使用方式
2)类模板在模板参数列表中可以有默认参数
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class NameType,class AgeType = int>
class person {
public:
NameType m_Name;
AgeType m_Age;
person(NameType name,AgeType age) {
this->m_Age = age;
this->m_Name = name;
}
void show() {
cout << "年龄:" << this->m_Age << endl;
cout << "名字:" << this->m_Name << endl;
}
};
void test1() {
//1.类模板没有自动类型推导的使用方式
//person p("Tom", 19); 错误的
person<string, int> p("Tom", 19);
p.show();
//2.类模板在模板参数列表中可以有默认参数,函数模板不行
person<string>p1("jerry", 10);
p1.show();
}
int main() {
test1();
return 0;
}
2.类模板中成员函数创建时机
1)普通类中的成员函数在编译的时候就可以创建
2)类模板中的成员函数在调用的时候才创建
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class person1 {
public:
void show1() {
cout << "person1" << endl;
}
};
class person2 {
public:
void show2() {
cout << "person2" << endl;
}
};
template<class T>
class Myclass {
public:
T obj;
void func1() {
obj.show1();
}
void func2() {
obj.show2();
}
};
void test1() {
//类模板中的成员函数在调用时才创建,所以上方并没有报错,即便他们是互相矛盾的
Myclass<person1>m;
m.func1();
//m.func2();
}
int main() {
test1();
return 0;
}
3.类模板对象做函数参数
1)指定传入的类型——直接显示对象的数据类型(常用)
2)参数模板化——将对象中的参数变为模板进行传递
3)整个类模板化——将这个对象类型模板化进行传递
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//类模板对象做函数参数
template<class T1,class T2>
class person {
public:
T1 m_Name;
T2 m_Age;
person(T1 name,T2 age) {
this->m_Age = age;
this->m_Name = name;
}
void show() {
cout << "年龄:" <<this->m_Age << endl;
cout << "名字:" << this->m_Name << endl;
}
};
//1.指定传入的类型
void print1(person<string, int>&p) {
p.show();
}
void test1() {
person<string, int>p("Tom", 10);
print1(p);
}
//2.参数模板化
template<class T1, class T2>
void print2(person<T1,T2>& p) {
p.show();
//可以借助这个函数检测模板参数的类型
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test2() {
person<string, int>p("Tom", 10);
print2(p);
}
//3.整个类模板化
template<class T>
void print3(T & p) {
p.show();
}
void test3() {
person<string, int>p("Tom", 10);
print3(p);
}
int main() {
test1();
test2();
test3();
return 0;
}
//其实可以理解为将参数的填入变得更加多元化。
4.类模板与继承
1)当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
2)如果不指定,编译器无法给子类分配内存
3)如果想灵活指定出父类中的T的类型,子类也需要变为类模板
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
template<class T>
class Base {
public:
T obj;
};
//当父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
//class Son1 :public Base 错误
class Son1 :public Base<int> {
public:
};
void test1() {
Son1 s1;
}
//如果想灵活指定父类中T的类型,子类也需变成类模板
template<class T1,class T2>
class Son2 :public Base<T2> {
public:
T1 t;
};
void test2() {
Son2<int ,char> s2;
}
int main() {
test1();
test2();
return 0;
}
5.类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:1)直接包含.cpp文件
2)将声明和实现写到同一个文件中,并更改后缀名为.hpp
.hpp是约定的名称,并不是强制。
#praga once 防止头文件重复包含
6.类模板成员函数类外实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class person {
public:
T1 m_Name;
T2 m_Age;
person(T1 name, T2 age);
void func();
};
//构造函数类外实现
template<class T1,class T2>
person<T1,T2>::person(T1 name, T2 age) {
this->m_Age = age;
this->m_Name = name;
}
//成员函数类外实现
template<class T1, class T2>
void person<T1, T2>::func() {
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test1() {
person<string, int>p("Tom", 20);
p.func();
}
int main() {
test1();
return 0;
}
7.类模板与友元
类模板配合友元函数类内和类外实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<string>
#include<iostream>
using namespace std;
//类外实现会比较复杂,所以最好还是类内实现
//要先让编译器知道有person这个类模板,还要让它知道personprint是一个带模板的全局函数
template<class T1,class T2>
class person;
//这里的却是一个类模板
template<class T1, class T2>
void personprint2(person<T1, T2> p) {
cout << "年龄:" << p.m_Age << endl << "姓名:" << p.m_Name << endl;
}
template<class T1,class T2>
class person {
//1,全局函数类内实现
friend void personprint(person<T1,T2> p) {//因为,person是一个类模板,数据类型是未知的,所以还是要带着模板
cout << "年龄:" << p.m_Age << endl << "姓名:" << p.m_Name << endl;
}
//2.全局函数类外实现
friend void personprint2<>(person<T1, T2> p);//普通函数
public:
T1 m_Age;
T2 m_Name;
person(T1 age, T2 name) {
this->m_Age = age;
this->m_Name = name;
}
};
void test1() {
person<string, int>p("Tom", 19);
personprint(p);
}
int main() {
test1();
return 0;
}