C++之类模板

2023-10-28

前言

类封装了属性和方法,而这些属性和方法都有他们自己的数据类型,在有些特殊场景,我们希望我们的类里的这些属性和方法的类型能够在使用的时候再指定,因为我们并无法事先判断这个类的使用者会传什么类型给到这个类,例如,一个集合类,用户可以往集合中存任意的数据类型,我们不可能为用户可能传的数据类型都做一个实现。那样太麻烦了,所以就出现了类模板。

1.类模板

语法:

template<class T>

解释:
template: 声明创建的模版
typename: 表面其后的类型是一种数据类型,让编译器不要报错
T:通用数据类型,名称可以自定义,通常为大写字母

注意:class 可以使用typename 代替,即声明模版时也可写成template<typename T>的方式,效果是一样的,只是有的人会在函数模版的情况下使用typename,在类模版下使用class.

示例:

#include<iostream>
#include<string>
using namespace std;

template<class NameType, class AgeType>
class Person
{
public:
    NameType mName;
    AgeType mAge;

    Person(NameType name,AgeType age){
        this->mName = name;
        this->mAge = age;
    }

    void showPerson(){
        cout << "name=>" << this->mName << " ,age=>"<<this->mAge<<endl;
    }
};

void test(){
    Person<string,int> p1("walt",28);
    p1.showPerson();
}
int main()
{
    test();
    return 0;
}

在上面的示例中,我们定义了一个Person类,类中有name和age两个属性,我们使用类模板的方式定义后可以使用类模版中定义的虚拟型来表示name和age的数据类型,调用的时候就可以传入任何想用的数据类型了,如果你想让名字以Int型表示,你就传int值,如:9527也可以表示一个人名字

注:此处的Person类只是作为演示使用,并不具备通用性

2.类模板和函数模版的区别

类模板和函数模板的区别有两点:
(1)类模板没有自动类型推导,函数模板有自动类型推导
(2)类模板在模板参数列表中可以有默认参数,而函数模板不行

两者的区别如下列所示:

#include<iostream>
using namespace std;

// 类模版和函数模版的区别
template<class NameType,class AgeType = int>
class Person
{
public:
    Person(NameType name,AgeType age){
        this->mName = name;
        this->mAge = age;
    }

    void showPerson()
    {
        cout << "name=>"<<mName << " ,age=>" << mAge<<endl;
    }

    NameType mName;
    AgeType mAge;
};

// 1,类模版没有自动类型推导的使用方式
void test()
{
    // Person p("walt",28);//错误,无法自动类型推导
    Person<string,int> p("walt",28);// 正确,只能使用显示指定类型
    p.showPerson();
}

// 2,类模版在模版参数列表中可以有默认参数
void test1()
{
    Person<string> p("zhong",29);
    p.showPerson();
}
int main()
{
    //test();
    test1();
    return 0;
}

3.类模板的使用

3.1 类模板对象作为函数参数

类模板对象作为函数参数有三种情况分别是指定传入类型,参数模板化和整个类模板化,其中指定传入类型最常用,具体的请看代码示例:

#include<iostream>
#include<string>
using namespace std;

// 类模版对象做函数参数

template<class T1,class T2>
class Person{
    public:
        Person(T1 name,T2 age){
            this->mName = name;
            this->mAge = age;
        }

        void showPerson()
        {
            cout<< "name=>"<< mName << " ,age=>" << mAge<<endl;
        }

        T1 mName;
        T2 mAge;
};
//1.指定传入类型: 最常用
void printPerson(Person<string,int> &p)
{
    p.showPerson();
}
// 2.参数模版化
template<class T1,class T2>
void printPerson1(Person<T1,T2> &p)
{
    p.showPerson();
    cout<<"type of T1 is: " << typeid(T1).name() << endl;
    cout<<"type of T2 is: " << typeid(T2).name() << endl;

}
// 3.整个类模版化
template<class T>
void printPerson2(T &p){
    p.showPerson();
    cout<< "the type of T is:" << typeid(T).name() << endl;
}

void test(){
    Person<string,int> p("walt",28);
    printPerson(p);
}

void test1(){
    Person<string,int> p("zhong",33);
    printPerson1(p);
}

void test2()
{
    Person<string,int> p("xianjian",29);
    printPerson2(p);

}

int main()
{
    //test();
    //test1();
    test2();
    return 0;
}

3.2 类模板与继承

当父类是类模板的时候,必须知道父类中定义的类型,才能继承给子类,并且如果想要灵活指定父类中的T类型,那么子类也需要变成类模板,请看示例代码:

#include<iostream>
using namespace std;

// 类模版与继承
template<class T>
class Base{
    T m;
};

//class Son : public Base{//错误,必须知道父类中T的类型,才能继承给子类

class Son:public Base<int>{//正确

};


// 如果想要灵活指定父类中的T类型,子类也需要变类模版
template<class T1,class T2>
class Son2:public Base<T2>{
    public:
    Son2()
    {
        cout<< "the type of T1 is: " << typeid(T1).name() <<endl;
         cout<< "the type of T2 is: " << typeid(T2).name() <<endl;
    }
    T1 obj; 
};

void test()
{
    Son s1;
}

void test1()
{
    Son2<int,char> s1;// int 给了T1,char 给了T2,给了父类
}

int main()
{
    test1();

    return 0;
}

3.3 类模板与友元

在C++ 中,外部想要访问类中的私有属性时,可以使用友元的方式,及将访问者添加friend关键字,标记为“朋友”。当访问的全局函数在类的内部实现时,直接访问就行,但是如果访问私有属性的友元函数在类外实现时,需要做到首先加空模板参数列表
friend void printPerson1<>(Person<T1,T2> p);
然后让编译器提前知道这个类模版的存在,即在文件的最前面声明下类模板

template<class T1, class T2>
class Person;

代码示例:

#include<iostream>
using namespace std;

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

// 通过全局函数 打印Person信息
template<class T1, class T2>
void printPerson1(Person<T1,T2> p)
{
    cout<<"out of class implement: name=>"<<p.mName << " ,age=>"<< p.mAge << endl;
}

template<class T1, class T2>
class Person
{
    // 全局函数类内实现
   friend void printPerson(Person<T1,T2> p)
    {
        cout<<"name=>"<<p.mName << " ,age=>"<< p.mAge << endl;

    }

    // 全局函数类外实现
    // 加空模版参数列表
    // 全局函数如果是类外实现,需要让编译器提前知道这个函数的存在

    friend void printPerson1<>(Person<T1,T2> p);
    public:
        Person(T1 name,T2 age){
            this->mName = name;
            this->mAge = age;
        }

    private:
        T1 mName;
        T2 mAge;

};
// 全局函数类内实现
void test()
{
    Person<string,int> p("Tom",13);
    printPerson(p);
}

// 全局函数的类外实现
void test1()
{
    Person<string,int> p("jerry",16);
    printPerson1(p);
}

int main()
{
    //test();
    test1();
    return 0;
}

3.4 类模板分文件编写

在某些场景下我们需要将类模板的定义和实现分文件编写。类模板的分文件编写步骤为:

  1. 先将类模板的定义和实现放到一起,命名为xxx.hpp

例如本例中的Person.hpp

#include<iostream>
#include<string>
using namespace std;

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

};

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
    this->mName = name;
    this->mAge = age;
}

template<class T1, class T2>
void Person<T1,T2>::showPerson()
{
    cout<<"name=>"<< mName << " ,age=>"<<mAge<<endl;

}
  1. 需要使用时,使用include关键字引入这个xxx.hpp这个文件就行

示例:

#include<iostream>
#include<string>
#include "header/Person.hpp"

using namespace std;

// 类模版分文件编写
void test()
{
    Person<string,int> p("jerry",18);
    p.showPerson();
}
int main()
{
    test();
    return 0;
}

总结

本文介绍了类模板的语法,类模板和函数模板的区别,以及类模板的使用,其中类模板的使用需要注意的是当类模板和继承,友元,函数的类内实现和类外实现,分文件编写一起使用的时候的注意点。本篇文章重点在于看懂,能使用类模板就行,不要求会写。函数模板和类模板知识的学习是为了更好的使用系统提供的模板进行开发。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之类模板 的相关文章

  • 如何在MVVM中管理多个窗口

    我知道有几个与此类似的问题 但我还没有找到明确的答案 我正在尝试深入研究 MVVM 并尽可能保持纯粹 但不确定如何在坚持模式的同时启动 关闭窗口 我最初的想法是向 ViewModel 发送数据绑定命令 触发代码来启动一个新视图 然后通过 X
  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • ASP.NET Core Serilog 未将属性推送到其自定义列

    我有这个设置appsettings json对于我的 Serilog 安装 Serilog MinimumLevel Information Enrich LogUserName Override Microsoft Critical Wr
  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 在 Visual Studio 2008 上设置预调试事件

    我想在 Visual Studio 中开始调试程序之前运行一个任务 我每次调试程序时都需要运行此任务 因此构建后事件还不够好 我查看了设置的 调试 选项卡 但没有这样的选项 有什么办法可以做到这一点吗 你唯一可以尝试的 IMO 就是尝试Co
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • C# 中的递归自定义配置

    我正在尝试创建一个遵循以下递归结构的自定义配置部分
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • 如何将单个 char 转换为 int [重复]

    这个问题在这里已经有答案了 我有一串数字 例如 123456789 我需要提取它们中的每一个以在计算中使用它们 我当然可以通过索引访问每个字符 但是如何将其转换为 int 我研究过 atoi 但它需要一个字符串作为参数 因此 我必须将每个字
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 如何使我的表单标题栏遵循 Windows 深色主题?

    我已经下载了Windows 10更新包括黑暗主题 文件资源管理器等都是深色主题 但是当我创建自己的 C 表单应用程序时 标题栏是亮白色的 如何使我自己的桌面应用程序遵循我在 Windows 中设置的深色主题 你需要调用DwmSetWindo
  • C++ fmt 库,仅使用格式说明符格式化单个参数

    使用 C fmt 库 并给定一个裸格式说明符 有没有办法使用它来格式化单个参数 example std string str magic format 2f 1 23 current method template
  • 控制到达非 void 函数末尾 -wreturn-type

    这是查找四个数字中的最大值的代码 include
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我

随机推荐