爱上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;
}
目录
相关文章
|
7天前
|
安全 编译器 C++
C++一分钟之-编译时计算:constexpr与模板元编程
【6月更文挑战第28天】在C++中,`constexpr`和模板元编程用于编译时计算,提升性能和类型安全。`constexpr`指示编译器在编译时计算函数或对象,而模板元编程通过模板生成类型依赖代码。常见问题包括误解constexpr函数限制和模板递归深度。解决策略包括理解规则、编写清晰代码、测试验证和适度使用。通过实战示例展示了如何使用`constexpr`计算阶乘和模板元编程计算平方。
30 13
|
4天前
|
存储 编译器 C++
【C++】详解C++的模板
【C++】详解C++的模板
|
2天前
|
C++ 开发者
C++一分钟之-编译时计算:constexpr与模板元编程
【7月更文挑战第2天】C++的`constexpr`和模板元编程(TMP)实现了编译时计算,增强代码效率。`constexpr`用于声明编译时常量表达式,适用于数组大小等。模板元编程则利用模板进行复杂计算。常见问题包括编译时间过长、可读性差。避免方法包括限制TMP使用,保持代码清晰。结合两者可以解决复杂问题,但需明确各自适用场景。正确使用能提升代码性能,但需平衡复杂性和编译成本。
13 3
|
11天前
|
编译器 C++
C++模板进阶
C++模板进阶
9 1
|
1天前
|
编译器 C语言 C++
【C++】模板初阶(下)
C++的函数模板实例化分为隐式和显式。隐式实例化由编译器根据实参推断类型,如`Add(a1, a2)`,但`Add(a1, d1)`因类型不一致而失败。显式实例化如`Add&lt;double&gt;(a1, d1)`则直接指定类型。模板函数不支持自动类型转换,优先调用非模板函数。类模板类似,用于创建处理多种数据类型的类,如`Vector&lt;T&gt;`。实例化类模板如`Vector&lt;int&gt;`和`Vector&lt;double&gt;`创建具体类型对象。模板使用时,函数模板定义可分头文件和实现文件,但类模板通常全部放头文件以避免链接错误。
|
1天前
|
机器学习/深度学习 算法 编译器
【C++】模板初阶(上)
**C++模板简介** 探索C++泛型编程,通过模板提升代码复用。模板作为泛型编程基础,允许编写类型无关的通用代码。以`Swap`函数为例,传统方式需为每种类型编写单独函数,如`Swap(int&)`、`Swap(double&)`等,造成代码冗余。函数模板解决此问题,如`template&lt;typename T&gt; void Swap(T&, T&)`,编译器根据实参类型推导生成特定函数,减少重复代码,增强可维护性。模板分函数模板和类模板,提供处理不同数据类型但逻辑相似的功能。
|
2天前
|
算法 编译器 程序员
|
2天前
|
存储 编译器 程序员
|
4天前
|
安全 C++
详细解读c++异常模板复习
详细解读c++异常模板复习
|
7天前
|
存储 算法 编译器
程序与技术分享:C++模板元编程简介
程序与技术分享:C++模板元编程简介