【C++】你想要的——印刷模板儿(下)

简介: 【C++】你想要的——印刷模板儿

3.模板函数和自定义函数

当模板函数和自己实现的函数是否可以同时存在时?

//专门处理int的加法函数
int Add(int left, int right) 
{
  return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right) 
{
  return left + right;
}
int main()
{
  int a = 1, b = 2;
  Add(a, b);
  Add<int>(a, b);
  return 0;
}

当自己写的函数和模板函数同时存在时,二者不会冲突,在之前我们讲过他们的函数名修饰规则是不同的。


同时存在,且调用时,首先会调用自己写的函数。因为模板函数相当于一个半成品,他需要推演实例化才会生成具体的函数,所以当然先使用自己实现的。


如果一定要使用模板函数的话,就需要显示实例化:Add(a,b);


这就叫泛型编程,与具体的类型无关!


2.类模板

类模板与函数模板不同的是:类模板统一显式实例化,不需要推演,或者说没有推演的时机,而函数模板实参传递形参时,就会发生推演实例化。


格式:

template<typename T>
class Stack
{
public:
  Stack(int capacity = 4)
  {
    _a = (T*)malloc(sizeof(T)*capacity);
    if (_a == nullptr)
    {
      perror("malloc fail");
      exit(-1);
    }
    _top = 0;
    _capacity = capacity;
  }
   ......
private:
  T* _a;
  int _top;
  int _capacity;
};
int main()
{
  // 显示实例化
  Stack<double> st1; // double
  st1.Push(1.1);
  Stack<int> st2; // int
  st2.Push(1);
  // s1,s2是同一个类模板实例化出来的,但是模板参数不同,他们就是不同类型
  return 0;
}

可能有人会问:s1=s2;  会不会发生隐式类型转换呢?当然不会,隐式类型转换只有在类型相近才会发生。

接下来创建一个数组类模板:

namespace mj
{
  template<class T>
  class array
  {
  public:
    inline T& operator[](size_t i)  //这里引用做返回值的目的是除了减少拷贝构造,还有可以修改返回值,直接可以修改数组里的值
    {
      assert(i < N);  //严格控制越界访问的情况
      return _a[i];
    }
  private:
    T _a[N];
  };
}
int main()
{
  mj::array<int> a1;
  for (size_t i = 0; i < N; ++i)
  {
    //相当于: a1.operator[](i)= i;
    a1[i] = i;
  }
  for (size_t i = 0; i < N; ++i)
  {
    // a1.operator[](i)
    cout << a1[i] << " ";
  }
  cout << endl;
  for (size_t i = 0; i < N; ++i)
  {
    a1[i]++;
  }
  for (size_t i = 0; i < N; ++i)
  {
    cout << a1[i] << " ";
  }
  cout << endl;
  return 0;
}

我们可以发现,类对象居然也可以使用数组那一套了??当然是取决于运算符重载。


他与普通数组最大的区别是:


1. 普通数组对于数组越界的这种情况,只能随机的抽查!而我们自己实现的类模板可以严格的控制越界访问这种情况!别说越界修改,越界访问都不行!


2.效率上因为[]是运算符重载,使用就会调用函数开辟栈帧,但是若定义到类中,并且加inline,就对于效率来说,那真是完美!


3.模板不支持分离编译

我们在实现数据结构的时候,是不是会经常去分几个文件去实现不同模块的功能?


(个人习惯).h文件中,我们写声明;.cpp文件中,我们写定义;test.cpp中,我们测试使用


但今天学习的模板就不能这么做了!!具体不能怎么做,我们上代码:


 

如果这样写的话,他就会报链接错误(就是在使用时找不到定义)


我们知道,在预处理阶段,就会将.h头文件展开,test.cpp中只有声明,在调用函数时,就会去找他的地址(call stack()),那么在编译的时候,编译器允许只有声明没有函数,相当于你可以先给他一个承诺,兑不兑现后面再说。


但在链接的时候,test.cpp中,却不能找到它的地址,这是为什么??这就是模板和其他的区别!


链接错误原因:


.cpp中的定义,不是实例化模板,他只是一个模板,没有任何实例化成任何类型。所以你在使用类模板的时候,压根就找不到它的定义,当然也找不到地址了,这不就链接错误了吗?


看上图:stack st; 显示实例化,但是.h中只有声明,test.cpp用的地方实例化了,但是定义的地方stack.cpp却没有实例化,只是一个模板。


用的地方在实例化,但是有声明,没有定义;


定义的地方没有实例化。


解决方法:


那转来转去就是一个问题:stack.cpp中定义没有实例化!!


办法一:


  你没有实例化,我给你补上:在定义后面加一个实例化

template<class T>
Stack<T>::Stack(int capacity = 4)
{
  cout << "Stack(int capacity = )" << capacity << endl;
  _a = (T*)malloc(sizeof(T)*capacity);
  if (_a == nullptr)
  {
    perror("malloc fail");
    exit(-1);
  }
  _top = 0;
  _capacity = capacity;
  template
  class Stack<int>;

但是就会有另一个问题,我如果使用的时候,创建不同类型,那模板实例化就要有不同类型,那就要一直补实例化,总不肯用一个补一个吧。


方法二:


那就是模板的编译不分离:(不要将定义和声明一个到.cpp,一个到.h)


当放在一个文件中时,在编译时,.h 文件展开后,定义和声明都在test.cpp中,那直接就会完成模板实例化,就有了函数地址,不需要再去链接了。


链接:只有声明没有定义才会到处去找定义。


那有人就会问,加inline可以吗?


inline当然不可以,加了inline后,直接不产生符号表,还存在什么地址吗?


直接放类中也不行,当数据量大的时候,都挤到一推,代码阅读性很差,会傻傻搞不清!


总结:


初阶模板,概念,分类,要注意的地方,这对后续的学习都是非常重要的,慢慢铺垫,厚积薄发!!

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