【C++】提高 -- 类模板

目录

一、类模板的作用

二、类模板的语法

三、类模板的例子

四、类模板和函数模板的区别

五、类模板中成员函数创建时机

六、类模板对象做函数参数

七、类模板与继承

八、类模板成员函数类外实现

九、类模板分文件编写

十、类模板与友元

十一、类模板案例


一、类模板的作用

建立一个通用类,类中成员、数据类型可以不具体指定,用一个虚拟的类型来代表

二、类模板的语法
cpp 复制代码
template<typename T>
//类
三、类模板的例子
cpp 复制代码
//类模板
template<class NameType, class AgeType>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this.m_Name = name;
        this.m_Age = age;
    }

    void showPerson()
    {
        cout << "姓名:" << this->m_name << "年龄:" << this->m_Age << endl;
    }
    
    NameType m_Name;
    AgeType m_Age;
};

void test01()
{
    person<string,int> p1 ("悟空",99);
    p1.showPerson();
}
四、类模板和函数模板的区别

①类模板没有自动类型推导的使用方法

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

cpp 复制代码
//类模板与函数模板的区别
template<class NameType, class AgeType = int>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this.m_Name = name;
        this.m_Age = age;
    }

    void showPerson()
    {
        cout << "姓名:" << this->m_name << "年龄:" << this->m_Age << endl;
    }
    
    NameType m_Name;
    AgeType m_Age;
};

void test01()
{
    //类模板没有自动类型推导的使用方式
    person<string,int>p1("悟空",99);//可以编译
    person p1 ("悟空",99);//不可以编译

    p1.showPerson();
}

void test02()
{
    //类模板在模板参数列表中可以有默认参数
     person<string>p2("八戒",9);//在模板中定义了该属性默认是一个int形

    p2.showPerson();
}
五、类模板中成员函数创建时机

在普通类中成员函数一开始就创建了;在类模板中成员函数在调用时才创建

cpp 复制代码
class Person1
{
public:
    void showPerson1()
    {
        cout << "Person1 show" << endl;
    }
};

class Person2
{
public:
    void showPerson2()
    {
        cout << "Person2 show" << endl;
    }
};

template<class T>
class Myclass
{
public:
    T obj;
    
    //类模板中的成员函数
    void func1()
    {
        obj.showPerson1();
    }

    void func2()
    {
        obj.showPerson2();
    }
}

当前函数代码段可以成功编译不报错,因为类模板中成员函数在调用时才创建所以编译时没调用不报错,obj并没有确定类型

cpp 复制代码
void test01()
{
    MyClass<Person1>m;
    m.func1();
    m.func2();
}

在上述代码段中添加该测试用例,编译器会报错,无法调用func2。

六、类模板对象做函数参数

类模板实例化出的对象向参数传参的方式一共有3种:

①指定传入的类型 -- 直接显示对象的数据类型 【最常用的方法】

②参数模板化 -- 将对象中的参数变为模板进行传递

③整个类模板化 -- 将这个对象类型 模板化进行传递

cpp 复制代码
template<class T1,class T2>
class Person
{
public:
    
    Person(T1 name,T2 age)
    {
        this.m_Name = name;
        this.m_Age = age;
    }

    void showPerson
    {
        cout << "姓名:" << this.m_Name << endl;
        cout << "年龄:" << this.m_Age << endl;
    }

    T1 m_Name;
    T2 m_Age;
}

//指定传入的类型
void printPerson1(Person<string,int>&p)
{
    p.showPerson();
}

void test01()
{
    Person<string,int>p("悟空",800);
    printPerson1(p);
}


//参数模板化
template<class T1,class T2>
void printPerson2(Person<T1,T2>&p)
{
    p.showPerson();
    //如何查看模板类型
    cout << "T1类型:" << typeid(T1).name() << endl; 
    cout << "T2类型:" << typeid(T2).name() << endl; 
}


void test02()
{
    Person<string,int>p("八戒",120);
    printPerson2(p);
}


//整个类模板化
template<class T>
void printPerson3(T &p)
{
    p.showPerson();
    cout << "T数据类型:" << typeid(T).name << endl;
    //会显示Person类型
}

void test03()
{
    Person<string,int>p("唐僧",100);
    printPerson3(p);
}
七、类模板与继承

注意事项:

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

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

cpp 复制代码
template<class T>
class Base
{
    T m;
};

class Son :public Base //此方法是错误的,必须要知道父类中的T的数据类型才能继承给子类
{
    
};

class Son :public Base<int>//此方法正确
{
    
};

void test01()
{
    T1 obj;
}

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

cpp 复制代码
template<class T>
class Base
{
    T m;
};

template<class T1,class T2>
class Son2 :public Base<T2>
{
public:

    Son2()
    {
        cout << "T1类型:" << typeif(T1).name << endl;
        cout << "T2类型:" << typeif(T2).name << endl;
    }

    T1 obj;
};

void test02()
{
    Son2<int, char>S2;
}
八、类模板成员函数类外实现
cpp 复制代码
//类内声明,类外实现
template<class T1,class T2>
class Person
{
public:
    
    Person(T1 name,T2 age);
    
    void showPerson();
 
    T1 m_Name;
    T2 m_Age;
};

//函数模板类外实现的构造函数
teemplate<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
        this.m_Name = name;
        this.m_Age = age;
}

//成员函数类外实现
teemplate<class T1,class T2>
void Person<T1,T2>::showPerson()
{
        cout << "姓名:" << this.m_Name << endl;
        cout << "年龄:" << this.m_Age << endl;
}
九、类模板分文件编写出现的常见问题与解决方法

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

解决方法:

①直接包含.cpp源文件

②将声明和实现写到同一个文件中,并更改后缀名为.hpp【hpp仅是约定的名字,可改】

第一步在头文件中添加新建项

第二步添加头文件 -- Person.h

其中Person.h内容为:

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;

template<class T1, class T2>
class Person
{
public:
    Person(T1 name, T2 age);

    void showPerson();

    T1 m_Name;
    T2 m_Age;

};

第三步在源文件中添加 -- Person.cpp

Person.cpp内容为:

cpp 复制代码
#include "Person.h"

template<class T1, class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
    this.m_Name = name;
    this.m_Age= age;

}

template<class T1, class T2>
Person<T1,T2>::showPerson()
{
    cout << "姓名:" << this.m_Name << "年龄:" << this.m_Age << endl;
}

第四步在需要使用的文件中输入以下代码:

部分编译器会出现报错无法正常生成代码[因为类模板中的成员函数是在调用时创建,编译器无法正确识别该代码],解决方法有以下:

解决方案1:将改行代码修改为 #include "Person.cpp",让编译器将.h和.cpp两个文件都阅读一次后再做接下来的解析,但是不建议使用该方式,会使二进制包变大

cpp 复制代码
#include <iostream>
using namespace std;
#include "Person.h"//解决方案1:将改行代码修改为 #include "Person.cpp",让编译器将.h和.cpp两个文件都阅读一次后再做接下来的解析,但是不建议使用该方式,会使二进制包变大

void test01()
{
    Person<string,int>p("ming",18);
    p.showPerson();
}

解决方案2:将.h文件中的内容和.cpp文件中的内容写一个.hpp文件中放在头文件中,声明和实现都在一个文件中【伪份文件编写】

相关内容可参考文章https://blog.csdn.net/HXG2006/article/details/128754137?ops_request_misc=&request_id=&biz_id=102&utm_term=.hpp%E5%92%8C.cpp%E5%8C%BA%E5%88%AB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-128754137.142^v100^pc_search_result_base4&spm=1018.2226.3001.4187

cpp 复制代码
#include <iostream>
using namespace std;
#include "Person.hpp"//解决方案2
void test01()
{
    Person<string,int>p("ming",18);
    p.showPerson();
}
十、类模板与友元

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

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

cpp 复制代码
//通过全局函数打印Person信息

//提前让编译器知道Person类存在
template<class T1,class T2>
class Person;

//全局函数不用加作用域
template<class T1,class T2>
friend void printPerson2(Person<T1,T2> p)
{
    cout << "类外实现 -- 姓名:" << p.m_Name << "类外实现 -- 年龄;" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
    //全局函数类内实现
    friend void printPerson(Person<T1,T2> p)
    {
        cout << "姓名:" << p.m_Name << "年龄;" << p.m_Age << endl;
    }
    
    //全局函数类外实现
    //需要加一个空模板的参数列表
    //如果全局函数是类外实现需要让编译器提前知道这个函数的存在
    friend void printPerson2<>(Person<T1,T2> p);  

public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }

private:
    T1 m_Name;
    T2 m_Age;
};
十一、类模板案例

实现以一个通用的数组类

相关推荐
奋斗的小花生38 分钟前
c++ 多态性
开发语言·c++
魔道不误砍柴功40 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
Nu11PointerException42 分钟前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
闲晨43 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程1 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
UestcXiye2 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
亦枫Leonlew2 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang