爱上c++的第八天(提高课程):模板

简介: 好了,结束了我们c++核心课程,进入到c++的提高课程,模板和STL部分,为什么先讲模板呢?因为我先学的模板,老师先教的模板,哈哈哈哈,废话文学功底见长,其实这本身就是有一个顺序的,哈哈哈。不皮了,我们开始下面的学习。

你的c++学习路上明灯


这几天会更的比较快,因为要做的事情比较多,我觉得,如果我选择了一些东西我就要为之努力,就像我在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.模板的局限性:模板的通用性并不是万能的(在某些特定的时候是不合适的)


812c21be5fd141169d599c993ee2d123.png

fe0835273e864ebba5554cbc6c5cd4f4.png


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;
}
目录
相关文章
|
1月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
93 10
|
3月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
4月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
30天前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
16 1
|
1月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
41 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
1月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
76 2
|
1月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
38 2
|
1月前
|
存储 算法 编译器
【C++】初识C++模板与STL
【C++】初识C++模板与STL
|
1月前
|
编译器 C++
【C++】模板进阶:深入解析模板特化
【C++】模板进阶:深入解析模板特化
|
2月前
|
存储 算法 程序员
C++ 11新特性之可变参数模板
C++ 11新特性之可变参数模板
54 0