C++类模板常见用途和注意实现

类模板定义

template<class T1,class T2 > //模板参数可以有一个或者多个
class 类名 {};

类模板和函数模板的区别

1,类模板不能自动类型推导,需要显示的指定类型;函数模板可以自动类型推导;

2,类模板在模板参数列表中可以有默认参数;函数模板中不能使用默认参数

#include<iostream>
using namespace std;

//类模板
template<class T1,class T2>
class Student {

public :
    T1 name;
    T2 age;

    Student(T1 name, T2 age) {
        this->name = name;
        this->age = age;
    }

    void show()
    {
        cout << name << age << "岁了"<<endl;
    }
};

//类模板中的模板参数列表可以指定默认参数.例如为模板参数T2设置默认参数int
template<class T1, class T2 =int>
class Student2 {

public:
    T1 name;
    T2 age;

    Student2(T1 name, T2 age) 
    {
        this->name = name;
        this->age = age;
    }

    void show()
    {
        cout << name << age << "岁了" << endl;
    }
};


void test1() {
    Student<string, int> s("王阳明", 100);//类模板必须显示的指定模板类型,不会隐式转换;
    s.show();
}

void test2() {//类模板中模板参数列表中设置了默认参数,使用类模板时不用再指定参数类型
    Student2<string> s("韩非子", 1001);
    s.show();
}

int main() {
    test1();
    test2();
    system("pause");
    return 1;
}

类模板成员函数的创建时机

类模板中的成员函数只有在调用的时候才会创建;但是普通类中的成员函数一开始就可以创建;

使用代码验证

#include<iostream>
using namespace std;

class Student1 {
public:
    void showMsg1() {
        cout << "我是Student1" << endl;
    }
};

class Student2 {
public:
    void showMsg2() {
        cout << "我是Student2" << endl;
    }
};

template<class T>
class  Test {

public:
    T obj;
    void showS1() {
        obj.showMsg1();
    }

    void showS2() {
        obj.showMsg2();
    }
};

int main() {
    Test<Student1> t;//如果不调用Test中的成员函数,可以编译通过,也就是验证了,类模板成员函数是在调用时创建;不调用不创建;

    t.showS1(); //到这里程序也能运行起来
        //t.showS2(); //如果调用成员函数showS2()就要报错,因为Student1中没有成员函数showS2();如果不调用showS2()程序就没问题,进一步说明类模板的成员函数再调用时才创建;
    return 1;
}

类模板作为函数参数

类模板最为函数参数有三种实现方式如下:

1,指定传入类型

//类模板最为函数参数三种方式发

//1,指定传入类型,指明类模板中模板参数具体类型;
void printStudent1(Student& s) {//注意变量为引用数据类型
      s.show();
}

//类模板作为参数的函数调用
void test1() {
    Student s("小米", 4);
    printStudent1(s);

}

2,参数模板化

//2,参数模板化,把类模板中模板参数模板化,不指定具体类型;其实就是函数模板加类模板的结合使用
template<typename T1,typename T2>
void printStudent2(Student<T1, T2>&s) {//注意变量为引用数据类型
    s.show();
}

void test2() {
    Student<string, int > s("小艾", 5);
    printStudent2(s);
}

3,整个类模板化

//3,整个类模板化,把类作为一个模板,

template<typename T>
void printStudent3(T& s) {//注意变量为引用数据类型
    s.show();
}

void test3() {

        Student s("小明", 3);

        printStudent3(s);

}

这个源文件如下:常用的还是第一种,比较直观,容易理解代码可读性强;

#include<iostream>
using namespace std;

template <typename T1,typename T2>
class Student
{
public :
    string name;
    int age;
    Student(T1 t1, T2 t2) {
        this->name = t1;
        this->age = t2;
    }

    void show() {
        cout << name << "同学今年" << age << "岁" << endl;
    }
};

//类模板最为函数参数三种方式发
//1,指定传入类型,指明类模板中模板参数具体类型;
void printStudent1(Student<string, int>& s) {//注意变量为引用数据类型
    s.show();
}

//类模板作为参数的函数调用
void test1() {

    Student<string, int > s("小米", 4);
        printStudent1(s);
}

//2,参数模板化,把类模板中模板参数模板化,不指定具体类型;其实就是函数模板加类模板的结合使用
template<typename T1,typename T2>
void printStudent2(Student<T1, T2>&s) {//注意变量为引用数据类型
    s.show();
}

void test2() {
    Student<string, int > s("小艾", 5);
    printStudent2(s);
}


//3,整个类模板化,把类作为一个模板,
template<typename T>
void printStudent3(T& s) {//注意变量为引用数据类型
    s.show();
}

void test3() {
    Student<string, int > s("小明", 3);
    printStudent3(s);
}

int main(){

    test1();
    test2();
    test3();

    return 1;
}

类模板最为函数参数,调用这个函数的方式都一样;

类模板继承

#include<iostream>
using namespace std;

template<class T>
class Animal {
public:
    T type;

    Animal() {
        cout << "T的类型:" << typeid(T).name() << endl;
    }
};

//类模板中的继承,需要制定父类模板参数的具体类型;语法规定
class Dog :public Animal<int> {


};

//不指定具体类型也可以,把子类也变为类模板,传模板参数到父类;这样在子类中也可以动态的指定父类的模板参数类型,可变的;具体如下
template<class T1,class T2>
class Cat :public Animal<T2> {

public:
    T1 name;
    void show() {//可以通过系统函数typeid()查看以下父类的T和子类的T1函数调用时传入的具体类型
        cout << "T1的类型:" << typeid(T1).name() << endl;
        cout << "T2的类型:" << typeid(T2).name() << endl;
        //cout << "T的类型:" << typeid(T).name() << endl; //注意T是父类的模板参数,这里不能判断是其类型,不然会报T未声明;
    }
};

int main() {
    Cat<string, int> cat;
    cat.show();

    return 1;
}

类模板的成员函数,类外实现

具体解释看代码及注释,很详细

#include<iostream>
using namespace std;

template<class T>
class Animal {
public:
    T name;

    Animal(T name);
    /*{
        cout << "T的类型:" << typeid(T).name() << endl;
    }*/

    void show();//成员函数声明
    /*{
        cout << "动物名字:" << name << endl;
    }*/
};
//成员函数类外实现:1,复制成员函数的声明;2,去掉“;”加上{} 3,添加作用域(Animal::);


template<typename T>//标识为模板
Animal<T>::Animal(T name)//<T>用于区分和其他模板函数的不同
{
    this->name = name;
}
template<typename T>
void Animal<T>::show() {// Animal<T>后面的<T>用于标识为类模板的成员函数;
    cout << "动物名字叫:" << name << endl;
}



int main() {
    Animal<string> a("小狗钱钱");
    a.show();

    return 1;
}

类模板分文件编写

类的模板函数的成员函数类外实现的分文件编写,即把声明部分写道Animal.h的头文件中;具体代码如下:

新建头文件Animal.h

#pragma once
#include<iostream>
using namespace std;
 
template<class T>
class Animal {
public:
    T name;

     Animal(T name);
     void show();//成员函数声明
};

Animal.cpp源文件中代码如下:

#include "Animal.h" //引入自定义的文件名使用""号

//成员函数类外实现:1,复制成员函数的声明;2,去掉“;”加上{} 3,添加作用域(Animal::);
template<typename T>//标识为模板
Animal<T>::Animal(T name)//<T>用于区分和其他模板函数的不同
{
    this->name = name;
}
template<typename T>
void Animal<T>::show() {// Animal<T>后面的<T>用于标识为类模板的成员函数;
    cout << "动物名字叫:" << name << endl;
}

测试类

Test.cpp

#include "Animal.cpp" //注意是导入的.cpp文件也就是源码文件,一般不会这样写,不然相当于把源码暴露给别人了;
int main() {
    Animal<string> a("小狗钱钱");
    a.show();

    return 1;
}

类模板的分文件编写第二种方法,把函数声明和实现都写在.hpp文件中;.hpp文件和头文件放在一起,是约定的一个文件格式不是固定的;具体如下

Animal.hpp

#include<iostream>
using namespace std;

template<class T>
class Animal {
public:
    T name;
    Animal(T name);
    void show();//成员函数声明
};


//成员函数类外实现:1,复制成员函数的声明;2,去掉“;”加上{} 3,添加作用域(Animal::);

template<typename T>//标识为模板
Animal<T>::Animal(T name)//<T>用于区分和其他模板函数的不同
{
    this->name = name;
}

template<typename T>
void Animal<T>::show() {// Animal<T>后面的<T>用于标识为类模板的成员函数;
    cout << "动物名字叫:" << name << endl;
}

Test.cpp文件中直接引入Animal.hpp文件即可调用;

类模板和友元

全局函数做友元类内实现,比较方便;但是如果类外实现需要先让编译器知道友元函数

#include<iostream>
using namespace std;

template<typename T1, class T2>
class Student;

//全局函数做友元,类外实现
template<class T1,class T2>
void showStudent2(Student<T1, T2> s)//这里用到Student类所以再次函数之前要先声明;
{
    s.print();//友元函数可以访问类中的私有成员方法和成员属性
}

template<typename T1,class T2>
class Student {
    //全局函数做友元,类内实现
    friend void showStudent(Student<string, int> s) //可以把模板参数列表换成T1,T2;
    {
        s.print();//友元函数可以访问类中的私有成员方法和成员属性
    }


    //全局函数做友元,类外实现 ,需要注意类外实现首先要告诉编译器,所以要在Student类之前实现showStudent2函数
    friend void showStudent2<>(Student<T1, T2> s);//注意需要加空模板参数列表<>

private:
    T1 name;
    T2 age;

    void print() {
        cout << "这位学生叫" << name << age << "岁了" << endl;
    }

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



int main() {
    Student<string,int> s("小度", 5);
    showStudent(s);

    Student<string, int> s2("小宝", 3);
    showStudent2(s2);
    return 1;
}

本文重点都在代码里面,注释很多,以免看不懂;

猜你喜欢

转载自blog.csdn.net/ezconn/article/details/129468318