掘根宝典之C++类模板大全

这篇具有很好参考价值的文章主要介绍了掘根宝典之C++类模板大全。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 类模板语法

类模板的作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表

语法:

template<typename T>
模板类声明


解释:
template声明创建模板
typename表明其后面的符号是一种数据类型,可以用class代替
T是通用的数据类型,名称可以替换,通常为大写字母

示例:

#include<iostream>
using namespace std;
//类模板
template<class NameType, class AgeType>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};


void test01()
{
    Person<string, int> p1("孙悟空", 999);
   //先创建了<string,int>版本的类定义,再将括号里的东西传进去
    p1.showPerson();
}
   
int main()
{
    test01();
    return 0;
}

运行结果是

name:孙悟空age:999

我们必须注意的一个点是仅在程序包含模板并不会生成模板类,必须请求实例化。也就是说,必须要将模板的泛型换为具体类型 

我们可能有点懵,来看个例子

#include<iostream>
using namespace std;
//类模板
template<class NameType, class AgeType>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};

   
int main()
{
    int a=10;
    return 0;
}

上面这个例子没有生成类定义,他只是告诉编译器如何生成类定义 ,这一点和函数模板是相同的

2 类模板与函数模板的区别

类模板与函数模板区别主要有两点:

1.类模板没有自动类型推导的使用方式
2.类模板在模板参数列表中可以有默认参数

示例://类模板与函数模板的区别

先看类模板的

template<class NameType, class AgeType = int> //指定默认参数
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
    }

    NameType m_Name;
    AgeType m_Age;
};


void test01()
{
    //Person p("孙悟空", 1000);错误的,类模板无法用自动类型推导
    Person<string, int>p("孙悟空", 1000);//正确,只能用显式指定类型推导
    p.showPerson();
}

void test02()
{
    Person<string>p("猪八戒", 999); //类模板在参数列表中有默认参数
}
   
int main()
{
    test01();
    system("pause");
    return 0;
}

再来看函数模板的

#include<iostream>
using namespace std;

//其实可以这么写template<class T=int>,但是不建议
template<class T>
void A(T a)
{
	cout << a << endl;
}
void B()
{
	A(3.0);//自动类型推断
}
int main()
{
	B();
}

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

类模板中成员函数和普通类中成员函数创建时机是有区别的:

1,普通类中的成员函数一开始就可以创建
2,类模板中的成员函数在调用时才创建
(因为我们写下模板类,只是告诉编译器如何去定义一个类,但是又不会创建类对象出来,只是一团虚的东西)

示例:

//类模板中成员函数的创建时机
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();
    }
};

void test01()
{
    Myclass<Person1>m;
    m.func1();
    //因为我们没有创建Person2的版本啊,所以也没有showPerson2这个函数啊
    //m.func2(); 无法调用
}

int main()
{
    test01();
    system("pause");
    return 0;
}

4 类模板对象做函数参数

学习目标:类模板实例化出的对象,向函数传参的方式

一共有三种传入方式:

指定传入的类型:直接显示对象的数据类型
参数模板化:将对象中的参数变为模板进行传递
整个类模板化:将这个对象类型模板化进行传递

示例:

//类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age)
    {
        this->m_Age = age;
        this->m_Name = name;
    }

    void showPerson()
    {
        cout << "name: " << this->m_Name << " age:" << this->m_Age << endl;
    }

    T1 m_Name;
    T2 m_Age;
};

//1、指定传入类型
void printPerson1(Person<string, int>&p)
{
    p.showPerson();
}
void test01()
{
    Person<string, int>p("孙悟空", 199);
    printPerson1(p);
}

   
// 2、参数模板化
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("猪八戒", 90);
    printPerson2(p);
}

// 3、整个类模板化
template<class T>
void printPerson3(T &p)
{
    p.showPerson();
    cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
    Person<string, int>p("唐僧", 60);
    printPerson3(p);
}

int main()
{
    test01();
    test02();
    test03();
    system("pause");
    return 0;
}


运行结果:

注:使用比较广泛的是指定传入类型的传参方式

5 类模板与继承

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

当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
如果不指定,编译器无法给子类分配内存
如果想灵活指定出父类中T的类型,子类也需为类模板

示例:

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

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

};

void test01()
{
    Son s1;
}

//如果想灵活指定父类中T的类型,子类也需要变成类模板
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 test02()
{
    Son2<int,char> s2;
}

int main()
{
    test02();
    
    system("pause");
    return 0;
}

6 类模板成员函数类外实现

示例:

//类模板成员类外实现
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 << " 年龄:" << this->m_Age << endl;
    }*/

    T1 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
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>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

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


int main()
{
    test01();
    
    system("pause");
    return 0;
}

7 类模板分文件编写

如果工程中需要利用多个类模板,那么将这些类模板都写在同一个文件中将会导致代码可读性变差,所以有必要对类模板进行分文件编写,但是类模板的分文件编写面临着一些问题,以下是类模板分文件编写面临的问题及解决方法。

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

解决:

解决方式1:直接包含.cpp源文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制的
示例1:(未进行分文件编写)

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 << " 年龄:" << this->m_Age << endl;
    }*/

    T1 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
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>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}

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


int main()
{
    test01();
    
    system("pause");
    return 0;
}


实例2:(进行分文件编写,利用.cpp)

1.创建头文件person.h,写一些声明

#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 m_Name;
    T2 m_Age;
};


2.创建person.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>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}


3.main函数编写
错误代码:

#include <iostream>
using namespace std;
#include <string>
#include "person.h"

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


int main()
{
    test01();
    
    system("pause");
    return 0;
}


注:因为如果包含person.h文件,那么编译器将会看到person.h中的代码。但是由于类模板中的成员函数一开始是不创建的,导致编译器没有看到person.cpp中的代码,所以执行test01时,无法解析其中的代码。

正确代码:(不常用)

#include <iostream>
using namespace std;
#include <string>
#include "person.cpp"

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


int main()
{
    test01();
    
    system("pause");
    return 0;
}


注:就是将person.h文件改成了person.cpp代码。编译器首先看到了person.cpp文件,因为person.cpp文件中有person.h文件,编译器又看到了person.h文件,所以能够解析test01中的代码。但是一般很少直接包含.cpp文件的,所以这个方法不常用。

实例3:(分文件编写,利用.hpp)
将person.h和person.cpp的内容写到一起,并将后缀名改为.hpp,这是类模板分文件编写最常用的方式

1.编写person.hpp文件:

#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 m_Name;
    T2 m_Age;
};

//构造函数的类外实现
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>
void Person<T1, T2>::showPerson()
{
    cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}


2.编写main函数

#include <iostream>
using namespace std;
#include <string>
#include "person.hpp"

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


int main()
{
    test01();
    
    system("pause");
    return 0;
}

8 类模板与友元

非模板友元

为模板类提供非模板友元函数,像下面这样子做就可以了

#include<iostream>
using namespace std;
void A(int a)//非模板友元函数
{
	cout << a << endl;
}
template<class T>
class AA
{
private:
	T a_;
public:
	AA(T a):a_(a){}
	friend void A(int a);//友元声明
};
int main()
{
	A(2);//正常使用
	//AA a(2);模板类不能自动类型推断
	AA<int> a(2);//OK
}

上面这个友元函数将成为所以模板实例化的友元 。例如它将是AA<float>和AA<char>等版本的友元函数。

假如我们要为友元函数提供模板类参数,可以像下面这样子做吗?

friend void A(AA&t);

答案是不行的,要提供模板类参数,必须指明具体化,像下面这两种做法都是可以的

template<class T>
class AA
{
friend void A(AA<T>&t);
friend void B(AA<int>&t);
....
}

 

模板类的约束模板友元函数

说白了就是来使类的每一个具体化都获得一个与友元匹配的具体化

全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在

1.全局函数的类内实现


template<class T1, class T2>
class Person
{
    //全局函数类内实现
    friend void printPerson(Person<T1, T2> p)
    {
        cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
    }
public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    
private:
    T1 m_Name;
    T2 m_Age;
};

void test01()
{
    Person<string, int>p("Tom", 30);
    printPerson(p);
}
int main()
{
    test01();
    
    system("pause");
    return 0;
}


2.全局函数类外实现

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

//类外实现
template<class T1, class T2>
void printPerson(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);
public:
    Person(T1 name, T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    
private:
    T1 m_Name;
    T2 m_Age;
};

void test01()
{
    Person<string, int>p("Tom", 30);
    printPerson(p);
}


int main()
{
    test01();
    
    system("pause");
    return 0;
}


注:需要注意各个函数声明之间的顺序。在Person类模板中有友元的声明friend void printPerson<>(Person<T1, T2> p),因为类模板中友元的类外实现需要让编译器提前知道这个函数,所以需要将printPerson函数写在前面。而printPerson函数中又涉及Person类,所以在printPerson函数前面需要提前声明Person类模板的存在。
总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别。

模板类的非约束模板友元函数

说白了就是每个函数具体化都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的

template<class T>
class AA
{
.....
template<class A,class B>feiend void A(A&,B&);
.....
}

模板的具体化

隐式实例化

到目前为止,上面所有例子用的都是隐式实例化,即它们声明一个或多个对象,指出所需类型,而编译器使用通用模板提供的处方生成具体的类声明

AA<int> a;

需要注意的是:需要对象之前,不会生成类的隐式实例化。

AA<int>*t;
t=new AA<int>;
//第二句导致编译器生成类定义,并根据该定义创建一个对象

显式实例化

显式实例化声明必须位于模板定义所在的名称空间里。

格式大概是

template class 类模板名<具体类型>;

我们看个例子

#include<iostream>
using namespace std;
void A(int a)
{
	cout << a << endl;
}
template<class T>
class AA
{
private:
	T a_;
public:
	AA(T a):a_(a){}
	friend void A(int a);
};

template class AA<int>;//注意不能放main函数里面

int main()
{

	A(2);
	//AA a(2);
	AA<int> a(2);
}

显式具体化

显式具体化是指在类模板外部对类模板的特定类型进行具体化。通过显式具体化,我们可以为模板类给出特定类型的定义,以覆盖默认的通用定义。

以下是一个示例,展示如何显式具体化一个类模板:

// 定义一个类模板
template <typename T>
class MyTemplate {
public:
    MyTemplate(T value) : m_value(value) {}

    void print() {
        std::cout << "Generic template: " << m_value << std::endl;
    }

private:
    T m_value;
};

// 显式具体化模板类的特定类型
template<>
class MyTemplate<int> {
public:
    MyTemplate(int value) : m_value(value) {}

    void print() {
        std::cout << "Specialized template for int: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    MyTemplate<double> obj1(3.14);
    obj1.print();  // 输出:Generic template: 3.14

    MyTemplate<int> obj2(42);
    obj2.print();  // 输出:Specialized template for int: 42

    return 0;
}

在上述示例中,定义了一个模板类 MyTemplate,它可以用于任意类型的参数 T。然后,通过显式具体化,我们为模板类 MyTemplate 的特定类型 int 提供了特殊化的定义,其中包含一个特定的成员函数 print,用于输出特定类型的值。

main 函数中,我们创建了两个 MyTemplate 的对象,一个是 MyTemplate&lt;double> 类型的,另一个是 MyTemplate&lt;int> 类型的。当调用它们的 print 成员函数时,将根据对象类型的特化定义,分别输出不同的结果。

这样,通过显式具体化,我们可以为特定类型提供自定义的定义,以满足特定的需求。文章来源地址https://www.toymoban.com/news/detail-828717.html

到了这里,关于掘根宝典之C++类模板大全的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 1万5千求职面试宝典大全ACCESS\EXCEL数据库

    《求职面试宝典大全ACCESS数据库》包含:1-穿着打扮、2-随身携带、3-场景指导、4-考官分析、5-考官问题、6-回答参考表、7-面试大全。虽然有些表的记录数不多,但胜在信息全面以及考官问题及答案的全面。 问题类型记录数为:待遇问题(10条)、个人素养(10条)、教育家庭

    2024年02月06日
    浏览(37)
  • [C++开发 03 _ 模板(167)]

    1.1模板的概念  模板的特点: 模板不可以直接使用,它只是一个框架 模板的通用并不是万能的 1.2函数模板 1.2.1函数模板语法 函数模板的引入: 函数模板的举例:  总结: 函数模板利用template 使用函数模板有两种方式:1、自动类型推导     2、显示指定类型 模板的

    2024年01月20日
    浏览(36)
  • C语言、c++史上最全最全爱心代码大全,彩色闪动、字符填充,附源码

    直接上代码: 直接上代码 直接上代码 直接上代码

    2024年02月04日
    浏览(40)
  • 【C++算法模板】图论-拓扑排序,超详细注释带例题

    推荐视频链接:D01 拓扑排序 给定一张 有向无环图 ,排出所有顶点的一个序列 A A A 满足:对于图中的每条有向边 ( x , y ) (x,y) ( x , y ) , x x x 在 A A A 中都出现在 y y y 之前,则称 A A A 是该图的顶点的一个拓扑序 拓扑排序 可以判断有向图中是否有环,可以生成拓扑序列 对于下

    2024年04月15日
    浏览(39)
  • C++、STL标准模板库和泛型编程 ——迭代器、 算法、仿函数(侯捷)

    侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! ! 一、C++ 面向对象高级开发 1、C++面向对象高级编程(上) 2、C++面向对象高级编程(下) 二、STL 标准库和泛型编程 1、分配器、序列式容器 2、关联式容器 3、迭代器、 算法、仿函数 4、适配器、补充 三、C++ 设计模式 四、C++ 新标准 五、

    2023年04月25日
    浏览(51)
  • 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题

    更多算法例题链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题) 什么是迭代器(iterator) 迭代器(iterator)的定义: 迭代器是一种检查容器内元素并遍历元素的数据类型。 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。 容器

    2024年04月14日
    浏览(48)
  • 基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码

    文后代码,优化效果图结尾处,最快3ms得到匹配结果 NCC,全称为Normalized Cross Correlation,即归一化互相关系数, 在模板匹配中使用的非常非常广泛,也是众多模板匹配方法中非常耀眼的存在, 这个匹配的理论核心基础公式如下: 其实Opencv的matchTemplate函数使用的就是这个公式

    2024年02月08日
    浏览(44)
  • 玄子Share-自然语言编程(NLP)_Java开发小白向 ChatGPT 提问的最佳模板

    以下内容均为 ChatGPT 回答 玄子: 我向你提问时,问题描述精确的重要性 ChatGPT 3.5 问题描述的精确性非常重要,因为它可以让回答者更好地理解您的问题,并且更容易提供准确和有用的解决方案。如果问题描述不够清晰或不够详细,回答者可能会误解您的问题或者理解不到位

    2023年04月09日
    浏览(45)
  • 算法宝典——Java版本(持续更新)

    注:由于字数的限制,我打算把算法宝典做成一个系列,一篇文章就20题!!! 目录 一、链表的算法题(目前10道) 1. 移除链表元素(力扣;思路:前后指针) 2. 反转一个单链表 (力扣;思路:头插法) 3. 链表的中间结点(力扣;思路:快慢指针) 4. 链表中倒数第k个结点

    2024年02月09日
    浏览(44)
  • C++面试宝典第19题:最长公共前缀

    题目         编写一个函数来查找字符串数组中的最长公共前缀,如果不存在公共前缀,返回空字符串\\\"\\\"。说明:所有输入只包含小写字母a-z。         示例1:         示例2: 解析         最长公共前缀(Longest Common Prefix)是计算机科学中一个常见的问题,它

    2024年01月19日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包