5,121
社区成员




类模板(Class Template)是 C++ 中的一种模板机制,用于创建通用类的框架,它允许程序员定义一个类时,类中的某些成员(如数据类型)可以是参数化的。 类模板 的核心作用是提高代码的复用性和灵活性,同时避免重复编写相似的代码。
类模板 的作用:
语法:
语法:template <class T> +
后紧跟 + 类声明或定义
紧跟的这一部分(类声明或定义)就叫做类模板
template ---> 声明创建模板
class---> 表示其后面的符号是一种自定义数据类型
T ---> 通用的数据类型(虚拟数据类型),名称可替换
- C++
- template<class T>--->模板参数列表
- 类
解释:
template — 声明创建模板
typename — 表其后面的符号是一种数据类型,可使用 class 代替
T — 通用的数据类型,名称可以替换,通常为大写做字母
重点:
类模板的基础实现:
- C++
-
- #include<iostream>
- using namespace std;
- #include<string>
-
- template<class NameType, class AgeType>
- class Person
- {
- public:
-
- Person(NameType name, AgeType age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- void shouPerson()
- {
- cout << "Name == " << this->Name
- << " Age == " << this->Age << endl;
- }
-
- NameType Name;
- AgeType Age;
- };
-
- void test01()
- {
- //类型参数化(将数据类型像参数一样传入模板的形参列表)
- // <> -- 模板的参数列表
- Person<string, int> p1("啦啦啦", 18);
- p1.shouPerson();
- }
- int main()
- {
- test01();
- return 0;
- }
总结:类模板 与 函数模板语法相似,在声明模板 template
后加 class
,此类就被称为类模板
类模板 与 函数模板区别主要有两点:
1.类模板中没有自动类型推导的使用
2.类模板在模板参数列表中可以有默认参数
- C++
- void test01()
- {
- Person p1("啦啦啦", 18);
- }
- C++
- void test01()
- {
- Person<string, int> p1("啦啦啦", 18);
- }
- C++
-
- //模板参数列表<> 默认参数 int
- template<class NameType, class AgeType = int>
- class Person
- {
- public:
- ...
- };
-
- void test02()
- {
- Person<string> p2("呦呦呦", 3);
- }
总结:
类模板中成员函数和普通成员函数创建时机是有区别的
- C++
-
- class Person1
- {
- public:
-
- void ShowPerson1()
- {
- cout << "Person1 Show" << endl;
- }
- };
-
- class Person2
- {
- public:
-
- void ShowPerson2()
- {
- cout << "Person2 Show" << endl;
- }
- };
-
- //虚拟类型(通用数据类型) T
- template<class T>
- class My_Class
- {
- public:
-
- //通过 虚拟类型(通用类)T 在模板类 My_Class 中实例化出了 "类对象 obj"
- T obj;
-
- //类模板中的成员函数
- void func1()
- {
- obj.ShowPerson1();
- }
-
- void func2()
- {
- obj.ShowPerson2();
- }
-
- //只要不去调用成员函数,func1() 以及 func2() ,func1()、func2()就不会被创建
- //因为 编译器无法确定被虚拟类型实例化出的对象 obj 到底是什么数据类型
- //一筹莫展
- //因此 这就导致了类模板中的成员函数在一开始不会被创建(因为函数自身也无法区分其属于什么数据类型),只有被调用时才会被创建
- };
-
- void test01()
- {
- My_Class<Person1> c1;
- c1.func1();
-
- c1.obj.ShowPerson1();
-
-
- My_Class<Person2> c2;
- c2.func2();
-
- c2.obj.ShowPerson2();
- }
- C++
-
- void test01()
- {
- My_Class<Person1> c1;
- c1.func1();
- c1.func2();//---> false
-
- c1.obj.ShowPerson1();
- }
总结:类模板中的成员函数并不是在一开始就创建的,其在被调用时才会被创建
学习目标:
一共有三种传入方式:
示例:
- C++
-
- //先声明一个类模板
- template<class TypeName, class TypeAge>
- class Person
- {
- public:
-
- Person(TypeName name, TypeAge age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- void ShowPerson()
- {
- cout << "姓名:" << this->Name
- << " 年龄:" << this->Age << endl;
- }
-
- TypeName Name;
- TypeAge Age;
- };
- C++
-
- //1、指定传入的类型 - 引用的方式传递,直接拿到 p1 的本体
- void printPerson1(Person<string, int>& p)
- {
- p.ShowPerson();
- }
- void test01()
- {
- Person<string, int> p1("啦啦啦", 18);
- printPerson1(p1);
- }
printPerson2
中的 <TypeName, TypeAge>
是模板中的两个参数 - C++
-
- void printPerson2(Person<TypeName, TypeAge>& p)
- {
- p.ShowPerson();
- }
-
- void test02()
- {
- Person<string, int> p1("呦呦呦", 3);
- printPerson2(p1);
- }
- C++
-
- template<class TypeName, class TypeAge>
- void printPerson2(Person<TypeName, TypeAge>& p)
- {
- p.ShowPerson();
- }
-
- void test02()
- {
- Person<string, int> p1("呦呦呦", 3);
- printPerson2(p1);
- }
typeid
查看虚拟类型 T
推导出的数据类型 - C++
- template<class TypeName, class TypeAge>
- void printPerson2(Person<TypeName, TypeAge>& p)
- {
- p.ShowPerson();
- cout << "TypeName 的数据类型为:" << typeid(TypeName).name() << endl;
- cout << "TypeAge 的数据类型为:" << typeid(TypeAge).name() << endl;
- }
- C++
-
- template<class T>
- void printPerson3(T& p)
- {
- p.ShowPerson();
- cout << "T 的数据类型为:" << typeid(T).name() << endl;
- }
-
- void test03()
- {
- Person<string, int> p("工藤新一", 18);
- printPerson3(p);
- }
总结:
当类模板碰到继承时,需要注意以下几个点:
T
的数据类型 T
的数据类型,子类也需要为类模板 示例:
- C++
-
- //类模板与继承
- template<class T>
- class Base {
- public:
- T b;
- };
-
- class Son : public Base--->false,必须要知道父类中 T 的类型,才能继承给子类
- {
- public:
-
- };
- C++
-
- //类模板与继承
- template<class T>
- class Base {
- public:
- T b;
- };
-
- class Son : public Base<int>
- {
- public:
-
- };
- C++
-
- //类模板与继承
- template<class T>
- class Base {
- public:
- T b;
- };
-
- template<class T1, class T2>
-
- class Son2 : public Base<T2>
- {
- public:
- T1 obj;
- };
-
- void test()
- {
- Son2<int, char> s2;
- }
将数据类型像参数一样进行传递
T
的数据类型 - C++
-
- template<class T>
- class Base {
- public:
- T b;
- };
-
- template<class T1, class T2>
-
- class Son2 : public Base<T2>
- {
- public:
-
- //构造函数 - 编译阶段(实例化对象时,开始被调用)
- Son2()
- {
- cout << "T1 的数据类型为:" << typeid(T1).name() << endl;
- cout << "T2 的数据类型为:" << typeid(T2).name() << endl;
- }
- T1 obj;
- };
-
- void test()
- {
- Son2<int, char> s2;
- }
总结:如果父类是类模板,那么子类需要指定出父类中 T
的数据类型
学习目标:掌握类模板成员函数的类外实现
- C++
-
- //类模板成员函数的类外实现
- template<class T1, class T2>--->模板参数列表
- class Person
- {
- public:
-
- Person(T1 name, T2 age);
-
- void ShowPerson();
-
- T1 Name;
- T2 Age;
- };
- C++
-
- //类模板 -- 构造函数的类外实现
- template<class T1, class T2>
- Person<T1, T2>::Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
- C++
- 1、类模板参数列表的声明(告诉编译器 Person() 中的 T1、T2 是模板类型)
- |
- template<class T1, class T2>
-
- 2、<T1, T2>告诉编译器这是一个类模板的类外实现
- |
- Person<T1, T2>::Person(T1 name, T2 age)
-
- 不然 Person::Person(T1 name, T2 age)--->就和不同的成员函数类外实现没区别了
- C++
-
- template<class T1, class T2>
- void Person<T1, T2>::ShowPerson()
- {
- cout << "姓名:" << this->Name
- << " 年龄:" << this->Age << endl;
- }
总结:类模板中的成员函数类外实现时,需要加入模板参数列表
学习目标:
问题:
解决方法:
.cpp
源文件 .hpp
(hpp 是约定的名称,而不是强制) Person.h
文件中
- C++
-
- #pragma once
- #include<iostream>
- using namespace std;
- #include<string>
-
- //分文件的编写问题,以及解决
- template<class T1, class T2>
- class Person {
- public:
-
- Person(T1 name, T2 age);
-
- void ShowPerson();
-
- T1 Name;
- T2 Age;
- };
Person.cpp
文件中
- C++
-
- #include"Person.h"
-
- //模板类 - 成员函数的类外实现
- template<class T1, class T2>
- Person<T1, T2>::Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- template<class T1, class T2>
- void Person<T1, T2>::ShowPerson()
- {
- cout << "姓名:" << this->Name
- << " 年龄:" << this->Age << endl;
- }
ClassT.cpp 文件中
- C++
-
- #include"Person.h"
-
- void test01()
- {
- Person<string, int> p("啦啦啦", 14);
- p.ShowPerson();
- }
- int main()
- {
- test01();
- system("pause");
- system("cls");
- return 0;
- }
ClassT.cpp 文件中
增添 #include"Person.cpp" 根据类模板函数创建时机 ----- “类模板中的成员函数在程序运行时,不会立即被创建,只有当其被调用时,才会进行创建”
在只包含 .h
文件时:
- C++
-
- #pragma once
- #include<iostream>
- using namespace std;
- #include<string>
-
- //分文件的编写问题,以及解决
- template<class T1, class T2>
- class Person {
- public:
-
- Person(T1 name, T2 age);
-
- void ShowPerson();
-
- T1 Name;
- T2 Age;
- };
编译器看到了如上代码,但其并不会在 .cpp
文件中生成这两个函数(Person()、ShowPerson()),编译器无法观察 / 产生到两函数的实现代码,并且直到代码运行结束,编译器也不会发现,两个函数的实现代码
这就导致了在链接阶段,无法解析外部命令
.p
头文件、.cpp
源文件写到一起,将文件后缀名更改为 .hpp
文件(即声明与实现(同床共枕)全在同一文件中),最后在 classT.cpp
文件中包含 #include”.hpp”
文件 学习目标:
全局函数类内实现 — 直接在类内声明友元即可
全局函数类外实现 — 需要提前让编译器知道全局函数的存在(会略显复杂)
示例:
- C++
-
- #include<iostream>
- using namespace std;
- #include<string>
-
- //通过全局变量,输出 Person 信息
-
- class Person {
-
- public:
-
- Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- void ShowPerson();
-
- private:
-
- T1 Name;
- T2 Age;
- };
- C++
-
- template<class T1, class T2>
- class Person {
-
- //全局函数 类内实现
- friend void PrintPerson(Person<T1, T2> p)--->PrintPerson()是一个全局函数
- {
- cout << "Name: " << p.Name
- << " Age: " << p.Age << endl;
- }
- /*
- 使用友元类 - friend:修饰 PrintPerson() 函数,使 PrintPerson() 作为 Person 类的好朋友(即,称为 Person 的友元函数),它不是 Person 中的成员函数,但其有资格去访问 Person 类的私有属性
-
- 并且,被关键字 friend 修饰的成员函数 PrintPerson() 会变为全局函数,作用域在全局,生命周期直至程序执行结束
- */
-
- public:
-
- Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- private:
-
- T1 Name;
- T2 Age;
- };
-
- //全局函数 类内实现
- void test01()
- {
- Person<string, int> p("啦啦啦", 18);
- }
如上述代码示例中,被关键词(友元)friend
所修饰的成员函数 PrintPerson()
是一个全局函数,同时它也被称为 Person 类的友元函数。此时,其不再是 Person
类的成员函数,但它可以访问 Person
类中的私有属性。
- C++
-
- template<class T1, class T2>
- class Person {
-
- //全局函数类外实现
- friend void PrintPerson02(Person<T1, T2>);
-
- public:
-
- Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- void ShowPerson();
-
- private:
-
- T1 Name;
- T2 Age;
- };
-
- //全局函数类外实现 - 无需作用域 Person:: (因为 PrintPerson02() 为全局函数),不需要多此一举加作用域
- template<class T1, class T2>
- void PrintPerson02(Person<T1, T2> p)
- {
- cout << "Name: " << p.Name
- << " Age: " << p.Age << endl;
- }
-
- void test02()
- {
- Person<string, int> p("呦呦呦", 3);
- PrintPerson02(p);
- }
调用产生 --- “ 无法解析外部命令 ”
原因:
- C++
-
- class
- {
- //因为类内声明的函数 PrintPerson02() 是一个普通(全局)函数
- friend void PrintPerson02(Person<T1, T2>);
- }
- C++
-
- //而类外实现的 PrintPerson02 是一个函数模板的实现
- template<class T1, class T2>
- void PrintPerson02(Person<T1, T2> p)
- {
- cout << "Name: " << p.Name << " Age: " << p.Age << endl;
- }
因此,这就导致了,两个 PrintPerson02
都不是一个东西
解决方法:为友元函数,增加一个空模板参数列表 <>
- C++
-
- class
- {
- friend void PrintPerson02<>(Person<T1, T2>);
- }
但仍需注意些许细节:如果想要全局函数(友元函数)在类外实现,还需让编译器得知友元函数的存在
将全局函数实现在 Person
类之前
但仍需注意:函数模板中的 Person
类模板,编译器也无法得知它的存在,因此,也许对类模板提前进行声明
- C++
-
- //提前声明 友元函数 PrintPerson02
- template<class T1, class T2>
- void PrintPerson02(Person<T1, T2> p)--->但友元函数的实现中,需要提供类模板 Person
- {
- cout << "Name: " << p.Name << " Age: " << p.Age << endl;
- }
- C++
-
- //因此,也需在 友元函数前,声明 类模板 Person
- template<class T1, class T2>
- class Person;
-
- template<class T1, class T2>
- void PrintPerson02(Person<T1, T2> p)
- {
- cout << "Name: " << p.Name
- << " Age: " << p.Age << endl;
- }
-
- template<class T1, class T2>
- class Person {
-
- //全局函数类外实现
- friend void PrintPerson02<>(Person<T1, T2>);
-
- public:
-
- Person(T1 name, T2 age)
- {
- this->Name = name;
- this->Age = age;
- }
-
- private:
-
- T1 Name;
- T2 Age;
- };
-
-
- void test02()
- {
- Person<string, int> p("呦呦呦", 3);
- PrintPerson02(p);
- }
文章来源: https://blog.csdn.net/2401_87692970/article/details/147256046
版权声明: 本文为博主原创文章,遵循CC 4.0 BY-SA 知识共享协议,转载请附上原文出处链接和本声明。