C++基础—模版

devtools/2024/10/22 18:42:47/

C++模板是C++语言中实现泛型编程的核心机制,它允许程序员定义通用的代码框架,这些框架在编译时可以根据提供的具体类型参数生成相应的特定类型实例。

泛型编程的特点代码复用和安全性!

模板主要分为两大类:函数模板类模板


函数模板

基本语法:

template <typename T, typename U, ...> 
函数声明或定义

语法解释:

template     ——声明创建模板

typename   ——表明其后面的 符号为一种数据类型,可以用class代替。

      T           ——这个是通用的数据类型,名称可以替换,通常为大写字母。

函数模板的注意事项:

1.模板类型推导(当函数模板被调用时,编译器会根据传递给模板函数的实际参数自动推导出相应的模板参数类型)时同一个通用类型参数要一致

2.函数模板本身并不生成任何代码。只有当模板被实例化,即编译器根据实际使用的参数类型确定出模板参数 T 的具体值时,才会生成对应的具体函数版本

普通函数和函数模板的区别:

a.普通函数调用时可以发生自动类型转换(隐式类型推导)

#include<iostream>
int main()
{int a{ 33 };char b = 'a';std::cout << a+b<< std::endl;return 0;
}//输出结果是
//130

 在a+b时发生了隐式类型转化,'a'转化为ASCN码值结果是33+97所以结果是130

b.函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

#include<iostream>
template<class T>
T add(T a, T b)
{return a + b;
}
int main()
{int a{ 33 };char b = 'a';std::cout <<add(a,b)<< std::endl;return 0;
}//结果出错!

这个会有错误,没有发生隐式转化

c.如果利用显式指定类型的方式,可以发生隐式类型转换

#include<iostream>
template<class T>
T add(T a, T b)
{return a + b;
}
int main()
{int a{ 33 };char b = 'a';std::cout <<add<int>(a,b)<< std::endl;return 0;
}//结果是
//130

 如果将这个代码修改这个,可以正确输出130

函数模版的局限性:

对于函数模板我们需要具体化模板

对于具体化的模板会被优先调用

#include<iostream>
template<class T>
void compare(T a,T b)
{if (a==b){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}class first
{
public:first(int a) :number(a) {};int number;
};int main()
{first s(88),ss(888);compare(s, ss);return 0;
}//这个会产生错误

如果具体化模版可以解决这个问题

#include<iostream>
template<class T>
void compare(T& a, T& b)
{if (a == b){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}class first
{
public:first(int a) :number(a) {};int number;
};//具体化模板的语法template<>        
void compare(first& a, first& b)
{if (a.number == b.number){std::cout << "same" << std::endl;}else{std::cout << "Notsame" << std::endl;}
}int main()
{first s(88),ss(888);compare(s, ss);return 0;
}//结果是
//Notsame

普通函数和函数模版的调用:

当普通函数与函数模板都能匹配调用时,编译器通常优先选择普通函数。这是因为普通函数更具体,而函数模板需要经过类型推导和实例化的过程。

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{int a{ 9 };put(a);return 0;
}//结果是
//普通函数的调用

若要强制调用函数模板,可以显式指定模板参数或者空模板参数列表

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{int a{ 9 };put<int>(a);  //put<>(a);return 0;
}//结果是
//模板函数的调用

函数模板也可以进行重载

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}template<class T>
void put(T a,T b)
{std::cout << "重载模板函数的调用" << std::endl;
}int main()
{int a{ 9 };int b{ 9 };put(a,b);return 0;
}//结果是
//重载模板函数的调用

 如果普通函数与模板函数之间模板函数有更好的选择,那会调用模板函数

#include<iostream>void put(int a)
{std::cout << "普通函数的调用" << std::endl;
}template<class T>
void put(T a)
{std::cout << "模板函数的调用" << std::endl;
}int main()
{char a = 'a';put(a);return 0;
}//结果是
//模板函数的调用

类模板

基本语法:

类模板的语法与函数模板一致

template <typename T, typename U, ...> 
函数声明或定义

语法解释:

template     ——声明创建模板

typename   ——表明其后面的 符号为一种数据类型,可以用class代替。

      T           ——这个是通用的数据类型,名称可以替换,通常为大写字母。

类模版和函数模版的区别:

1.类模板没有自动类型推导的使用方式(在C++17后有自动推导的使用方式)

正确的举例:

#include<iostream>
#include<string>template<class T,class Y>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first <int, std::string>secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}//输出的结果
44  haha

在C++17之前如果不进行显示类型指示,那会发生错误

举例子:

#include<iostream>
#include<string>
template<class T,class Y>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}

2.类模板在模板参数列表中可以有默认参数

#include<iostream>
#include<string>
template<class T,class Y=int>
class first
{
public:first(T a,Y b):A(a),B(b){}T A;Y B;
};int main()
{first <int,std::string>secend(44, "haha");std::cout << secend.A << "  " << secend.B;return 0;	
}

类模板的默认值与函数参数的默认值的很相似,如果进行了显示类型指示那就会覆盖默认值,如果一个参数被设立了默认值,那这个参数右边所有的参数有都需设立为默认。

类模版成员函数的创建时间

1.普通类中的成员函数一开始就可以创建

2.类模板中的成员函数在调用时才创建

类模板中的成员函数并不是一开始就创建的,在调用时才去创建

类模版作为函数参数

1.指定传入的类型——直接显式对象的数据类型

#include <iostream>  template <class T>
class MyClass
{
public:T value;MyClass(T val) : value(val) {}
};void printValue(MyClass<int> &obj)
{std::cout << "Value: " << obj.value << std::endl;
}int main()
{MyClass myIntObject(42);printValue(myIntObject);return 0;
}//输出结果是
Value: 42

2.参数模板化——将对象中的参数变为模板参数进行传递

#include <iostream>  template <class T>  
class MyClass 
{  
public:  T value;  MyClass(T val) : value(val) {}  
};template <class T>  
void printValue(MyClass<T> obj) 
{  std::cout << "Value: " << obj.value << std::endl;  
}  int main() 
{  MyClass<int> myIntObject(42);  MyClass<double> myDoubleObject(3.14);  printValue(myIntObject);  // 自动推导为 MyClass<int>  printValue(myDoubleObject); // 自动推导为 MyClass<double>  return 0;  
}

3.整个类模板化——将这个对象类型,模板化进行传递 

#include <iostream>  template <class T>
class MyClass
{
public:T value;MyClass(T val) : value(val) {}
};template <class T>
void printValue(T obj)
{std::cout << "Value: " << obj.value << std::endl;
}int main()
{MyClass<int> myIntObject(42);MyClass<double> myDoubleObject(3.14);printValue(myIntObject);  // 自动推导为 MyClass<int>  printValue(myDoubleObject); // 自动推导为 MyClass<double>  return 0;
}//输出是
Value: 42
Value: 3.14

类模板成员在类外实现

#include<iostream>
#include<string>
template<class T,class Y>
class first
{
public:first(T a, Y b);void show();T A;Y B;
};//类模板成员函数类外实现
template<class T,class Y>
first<T,Y>::first(T a,Y b)
{A = a;B = b;
}//成员函数类外实现
template<class T,class Y>
void first<T,Y>::show()
{std::cout <<A<<"   " << B;
}int main()
{first <int,std::string>secend(44, "haha");secend.show();return 0;	
}//输出的结果是:
44   haha

类模板中成员函数类外实现时,需要加上模板参数列表

类模板与继承

当类模板碰到继承时,需要注意一下几点:

当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型

如果不想指定,编译器无法给子类分配内存

#include<iostream>
template<class T>
class first
{
public:T a;
};class secend :public first
{
public:};int main()
{class secend AA(33);
}//会出现错误

如果想灵活指定出父类中T的类型,子类也需要变为类模板

#include<iostream>
template<class T>
class first
{
public:T a;
};class secend :public first<int>
{
public:};int main()
{class secend AA(33);
}//不会出错

将子类也定义为模板

#include<iostream>
template<class T>
class first
{
public:T a;
};template<class T,class Y>
class secend :public first<Y>
{
public:T aa;Y b;secend(T aaval, Y bval, Y firstaval): first<Y>(firstaval), aa(aaval), b(bval) {}
};int main()
{class secend <int ,int>AA(33,44,55);
}

类模板与友元

掌握类模板配合友元函数的类内和类外实现

全局函数类内实现,直接在类内声明友元即可、

全局函数类外实现,需要提前让编译器知道全局函数的存在

#include <iostream>  template <typename T>  
class MyClass {  
public:  MyClass(T value) : data(value) {}  // 声明友元函数  friend void printData(const MyClass<T>& obj);  private:  T data;  
};  // 全局函数类内实现(直接在类定义中定义)  
template <typename T>  
void printData(const MyClass<T>& obj) {  std::cout << "Data: " << obj.data << std::endl;  
}  int main() {  MyClass<int> myObject(42);  printData(myObject); // 输出:Data: 42  return 0;  
}
#include <iostream>  // 前置声明模板类  
template <typename T> class MyClass;  // 前置声明全局友元函数  
template <typename T>  
void printData(const MyClass<T>& obj);  template <typename T>  
class MyClass {  
public:  MyClass(T value) : data(value) {}  // 声明友元函数  friend void printData<>(const MyClass<T>&);  private:  T data;  
};  // 全局函数类外实现  
template <typename T>  
void printData(const MyClass<T>& obj) {  std::cout << "Data: " << obj.data << std::endl;  
}  int main() {  MyClass<int> myObject(42);  printData(myObject); // 输出:Data: 42  return 0;  
}

类模板份文件书写

在C++中,类模板通常涉及多个文件的组织:头文件(.h 或 .hpp)用于声明模板类,源文件(.cpp)用于实现模板类的方法。但是,由于类模板的特殊性,其声明和实现通常合并到同一个头文件中。

如果将模板的声明放在头文件中,而实现放在源文件中,当其他源文件通过包含头文件使用模板时,编译器无法看到模板的实现,导致无法完成模板实例化。这与普通类不同,普通类的成员函数实现可以分离在源文件中,因为编译器只需要知道类的接口(声明)即可编译依赖该类的代码。


http://www.ppmy.cn/devtools/30651.html

相关文章

SpringBoot-@Transactional注解失效

Transactional注解失效 Transactional失效场景 以下是一些常见导致Transactional注解失效的场景&#xff0c;配合相应的Java代码演示&#xff1a; 1、方法修饰符非公开&#xff08;非public&#xff09; Transactional注解失效的原因在于Spring事务管理器如何实现对事务的代…

springboot2.6.7集成springfox3.0.0

springboot2.6.7集成springfox3.0.0 1. pom配置2. 增加swagger自动配置类3. 配置修改4. 自动配置类增加以下内容参考 1. pom配置 <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui --> <dependency><groupId>io.springfox</g…

机器学习入门之非监督学习和半监督学习

文章目录 非监督学习半监督学习机器学习的核心价值 非监督学习 与监督学习相反&#xff0c;非监督学习的训练数据集是完全没有标签的数据&#xff0c;他本质上所做的工作都是聚类的 给定数据之后&#xff0c;聚类能从中学习到什么&#xff0c;就完全取决于数据本身的特性的&a…

上海计算机学会2022年4月月赛C++丙组T3平衡括号(简)

题目描述 给定一个只包含 ( 与 ) 的括号序列&#xff0c;请删除尽量少的括号&#xff0c;使它变成平衡的。平衡的定义如下&#xff1a; 空序列是平衡的&#xff1b;如果某个括号序列 s 是平衡的&#xff0c;那么 (s) 也是平衡的&#xff1b;如果某两个括号序列 s 与 t 都是平…

【机器学习】机器学习学习笔记 - 监督学习 - KNN线性回归岭回归 - 02

监督学习 KNN (k-nearest neighbors) KNN 是用 k 个最近邻的训练数据集来寻找未知对象分类的一种算法 from sklearn import neighbors# 分类 # 创建KNN分类器模型并进行训练 classifier neighbors.KNeighborsClassifier(num_neighbors, weightsdistance) classifier.fit(X,…

处理分支更新与pull操作

处理分支更新与pull操作 问题描述 There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details.git pull <remote> <branch> If you wish to set tracking information for th…

【免费AI系统】智狐AIs:企业级AI解决方案,提升您的工作效率

今天&#xff0c;我将为您介绍一个创新的AI平台——智狐AIs&#xff0c;这是一个致力于让AI技术变得易于接触和使用的平台&#xff0c;它为不同层次的用户提供了一个功能强大且易于操作的交互环境。 智狐AIs&#xff1a;您智能生活的新伙伴 智狐AIs以其简洁而强大的设计&#…

wpf 树形结构

Simplifying the WPF TreeView by Using the ViewModel Pattern - CodeProject 【原创】WPF TreeView带连接线样式的优化&#xff08;WinFrom风格&#xff09; - iDream2016 - 博客园 (cnblogs.com)