【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;
};
十一、类模板案例

实现以一个通用的数组类

相关推荐
maxruan7 小时前
PyTorch学习
人工智能·pytorch·python·学习
MYX_3097 小时前
第三章 线型神经网络
深度学习·神经网络·学习·算法
_李小白7 小时前
【Android Gradle学习笔记】第八天:NDK的使用
android·笔记·学习
ftpeak7 小时前
JavaScript性能优化实战
开发语言·javascript·性能优化
一个很帅的帅哥8 小时前
JavaScript事件循环
开发语言·前端·javascript
驰羽8 小时前
[GO]gin框架:ShouldBindJSON与其他常见绑定方法
开发语言·golang·gin
摇滚侠8 小时前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠8 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
程序员大雄学编程8 小时前
「用Python来学微积分」5. 曲线的极坐标方程
开发语言·python·微积分
沐怡旸8 小时前
【穿越Effective C++】条款02:尽量以const, enum, inline替换#define
c++·面试