欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++类模板

程序员文章站 2022-06-01 11:55:10
...

 1 类模板

    类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个类,

其功能是相同的,仅仅是数据类型不同。

    1.类模板用于实现类所需数据的类型参数化

    2.类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算

法不受所包含的数据类型的影响。

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Filename: 4 类模板
 *Author: Young Fan
 *Date: 2018.4.30
 *IDE: Visual Studio 2017
 *Description: 类模板
 */

#include<iostream >
using namespace std;

template<class T>
class Person
{
public:
	Person(T id,T age)
	{
		this->mAge = age;
		this->mId = id;
	}

	void Show()
	{
		cout << "ID:" << mId << "  Age:" << mAge << endl;
	}
public:
	T mId;
	T mAge;
};

void test1()
{
	//函数模板在在调用的时候,可以自动类型推导
	//但,类模板必须显式指定类型
	Person<int> p(10, 20); //必须显式指定类型。因为类定义对象,对象要需要编译器分配内存
	p.Show();
}
 
int main()
{
	test1();
	return 0;
}

总结

      函数模板在在调用的时候,可以自动类型推导,但,类模板必须显式指定类型如:Person<int> p(10, 20); 必须显式指定类型。因为类定义对象,对象要需要编译器分配内存

1.1 类模板派生普通类

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Filename: 5 类模板派生普通类
 *Author: Young Fan
 *Date: 2018.5.2
 *IDE: Visual Studio 2017
 *Description: 类模板派生普通类
 */

#include<iostream>
using namespace std;

template<class T>
class Person
{
public:
	Person()
	{
		mAge = 0;
	}
public:
	T mAge;
};

//类定义对象,对象要需要编译器分配内存
//所以必须显式指定类型
class subPerson:public Person<int> //必须显式指定类型。因为类定义对象,对象要需要编译器分配内存
{

};

int main()
{
	
	return 0;
}

总结:

函数模板在在调用的时候,可以自动类型推导但,类模板必须显式指定类型,必须显式指定类型。因为类定义对象,对象要需要编译器分配内存。故在继承(派生)类的时候要指定类型。

 

1.2 类模板派生类模板

 

 

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Filename: 6 类模板派生类模板
 *Author: Young Fan
 *Date: 2018.5.2
 *IDE: Visual Studio 2017
 *Description: 类模板派生类模板
 */

#include<iostream>
using namespace std;

template<class T>
class Animal
{
public:
	void Jiao()
	{
		cout << mAge << "岁动物在叫!" << endl;
	}
public:
	T mAge;
};

template<class T>
class Cat : public Animal<T>
{

};


int main()
{
	Cat<int> cat;
	cat.mAge = 2;
	cat.Jiao();

	return 0;
}

 

 

1.3 类的h与cpp分离编写

Person.h

//C语言中防止头文件被重复包含,如下方式
#ifndef PERSON_H  //防止头文件被重复包含,因为包含一次就行
				//Ctrl + shif + u,可以将选中英文字符内容变大写,如这里讲person_h变为PERSON_H
#define PERSON_H

//..................

#endif

//C++中防止头文件被重复包含用#pragma once
#pragma once

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	Person(string name, int age);  //构造函数声明
	void Show(); //方法声明
public:
	string mName;
	int mAge;
	int mID;
};

//.h里只做声明,不做定义(实现),.cpp里做定义(实现)

Person.cpp

 

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.6.22
 *IDE: Visual Studio 2017
 *Description: 
 */

#include "Person.h"

Person::Person(string name, int age)  //构造函数定义(实现),要加类的作用域
{
	this->mName = name;
	this->mAge = age;
}

void Person::Show()  //方法的定义(实现)
{
	cout << "Name:" << this->mName << "  Age:" << this->mAge << endl;
}


main.cpp

 

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.6.22
 *IDE: Visual Studio 2017
 *Description: 
 */

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

int main()
{
	Person p("YoungFan", 25);
	p.Show();

    return 0;
}

 

1.4 类模板实现-在类内实现

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.6.22
 *IDE: Visual Studio 2017
 *Description: 
 */

#include <iostream>
#include <string>  //用string要加头文件

using namespace std;

template<class T1,class T2>
class Person
{
public: //构造函数这里也得加权限,不加默认为private
	Person(T1 name, T2 age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void Show()
	{
		cout << "Name:" << this->mName << "  Age:" << this->mAge << endl;
	}
public:
	T1 mName;
	T2 mAge;
};

void test()
{
	Person<string, int> p("YoungFan",25);
	p.Show();
}

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

构造函数也得加权限,不加默认为private

 

1.5 类模板的外部实现

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.6.22
 *IDE: Visual Studio 2017
 *Description: 
 */

#include <iostream>

using namespace std;

//普通友元的类外声明,很麻烦,所以不要滥用友元
template<class T> class Person; //类声明
template<class T> void PrintPerson(Person<T>& p); //函数声明

template<class T>
class Person
{
public: 
	Person(T age, T id); //声明。编译器碰到声明不编译,会生成一个函数符号。在连接的时候
						//,根据这个符号去找函数定义(实现),找到就连上,找不到就连接失败
						//,连接失败的话就会报无法解析的外部符号
	void Show();

	//重载左移操作符,这里使用友元
	//方法一:加template<class T>,这种方法在vs下编译可以通过并执行,但在其他编译器如Linux不能编译通过
	//代码如下所示
	//template<class T>
	//friend ostream& operator<<(ostream& os, Person<T>& p);

	//方法二:不加template<class T>,加<T>
	friend ostream& operator<<<T>(ostream& os, Person<T>& p);

	//普通友元函数
	//方法一:加template<class T>,这种方法在vs下编译可以通过并执行,但在其他编译器如Linux不能编译通过
	//代码如下所示
	//template<class T>
	//friend void PrintPerson(Person<T>& p);

	//方法二:不加template<class T>,加<T>,还要写类外声明
	friend void PrintPerson<T>(Person<T>& p);

private:
	T mID;
	T mAge;
};

//类外定义
template<class T>
Person<T>::Person(T age, T id) //使用类模板需要模板参数列表,即Person<T>::
{
	this->mID = id;
	this->mAge = age;
}

template<class T>
void Person<T>::Show() //使用类模板需要模板参数列表,即Person<T>::
{
	cout << "ID:" << this->mID << "  Age:" << this->mAge << endl;
}

//重载左移运算操作符
template<class T>
ostream& operator<<(ostream& os, Person<T>& p)
{
	os << "Age:" << p.mAge << " ID:" << p.mID << endl; //因为使用的友元,故能直接获取私有成员
	return os;
}

template<class T>
void PrintPerson(Person<T>& p)
{
	cout << "Age:" << p.mAge << " ID:" << p.mID << endl;
}

void test()
{
	Person<int> p(10, 20);
	p.Show();
	cout << p; //使用重载的操作符“<<” 
	PrintPerson(p);
}

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

1.6 类模板h与cpp分离编写

C++类模板

C++类模板

Person.h

//这里使用C语言的防止头文件被重复包含
//C++中防止头文件被重复包含用#pragma once

#ifndef PERSON_H
#define PERSON_H

#include <iostream>
using namespace std;
template<class T>
class Person
{
public:
	Person(T age);
	void Show();
private:
	T age;
};

#endif

Person.hpp

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.7.8
 *IDE: Visual Studio 2017
 *Description: 
 */

#include "Person.h"

//函数模板  ,它经过两次编译
//并没有生成独立的具体模板函数(因为是独立编译的,没有调用,即没有调用设置类型)
template<class T>
Person<T>::Person(T age)
{
	this->age = age;
}

template<class T>
void Person<T>::Show()
{
	cout << "Age: " << age << endl;
}

main.cpp

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.7.8
 *IDE: Visual Studio 2017
 *Description: 
 */

//#include "Person.h"  //不能包含.h
//#include "Person.cpp"  //要包含cpp,相当于把该cpp拷进来了
#include "Person.hpp" //把头文件的那个cpp文件后缀改为hpp(相当于头文件和cpp文件的合体)
					//然后,在这里包含hpp文件,一般有类模板分文件的时候都这么写
//分离写法的时候不要涉及友元,会不通过,具体不知原因

int main()
{
	Person<int> p(10);//构造函数的定义在当前文件没有找到,编译器认为这个函数在其他文件中定义
	            //让链接器在连接的时候,去找这个函数的具体位置
	p.Show(); //Show函数的定义在当前文件没有找到,编译器认为这个函数在其他文件中定义
		//让链接器在连接的时候,去找这个函数的具体位置

    return 0;
}

//在写类模板的时候,最好写在一个文件里
//但如果要分开为多文件,就把头文件的那个cpp文件后缀改为hpp(相当于头文件和cpp文件的合体)
//所以在写类模板的时候写为hpp,别人一看见就知道里面有类模板

总结:

#include "Person.hpp" 把头文件的那个cpp文件后缀改为hpp(相当于头文件和cpp文件的合体)

然后,在这里包含hpp文件,一般有类模板分文件的时候都这写

分离写法的时候不要涉及友元,会不通过,具体不知原因

 

Person<int> p(10);//构造函数的定义在当前文件没有找到,编译器认为这个函数在其他文件中定义,让链接器在连接的时候,去找这个函数的具体位置

p.Show(); //Show函数的定义在当前文件没有找到,编译器认为这个函数在其他文件中定义,让链接器在连接的时候,去找这个函数的具体位置

 

在写类模板的时候,最好写在一个文件里,但如果要分开为多文件,就把头文件的那个cpp文件后缀改为hpp(相当于头文件和cpp文件的合体),所以在写类模板的时候写为hpp,别人一看见就知道里面有类模板

 

 

1.7  类模板碰到static成员

    从类模板实例化的每一个模板类有自己的类模板数据成员,该模板的所有对象共享一个static 数据成员。

    和非模板类的static 数据成员一样,模板类的static 数据成员也应该在文件范围定义和初始化。

    每个模板类有自己类模板的static 数据成员的副本。

/**
 *Copyright (c) 2018 Young Fan.All Right Reserved.
 *Author: Young Fan
 *Date: 2018.7.8
 *IDE: Visual Studio 2017
 *Description: 
 */

#include <iostream>
using namespace std;

template<class T>
class Person
{
public:
	static int a;
};

//类外初始化
template<class T> int Person<T>::a = 0;

int main()
{
	Person<int> p1, p2, p3;
	Person<char> pp1, pp2, pp3;

	p1.a = 10;
	pp1.a = 100;

	cout << p1.a << " " << p2.a << " " << p3.a << endl; //打印:10 10 10
	cout << pp1.a << " " << pp2.a << " " << pp3.a << endl;//打印: 100 100 100

	//1.从类模板实例化的每一个模板类有自己的类模板数据成员,该模板的所有对象共享一个static 数据成员

	//2.故每个模板类有自己类模板的static数据成员的副本,即具有独有的static成员

    return 0;
}

总结:

1.从类模板实例化的每一个模板类有自己的类模板数据成员,该模板的所有对象共享一个static 数据成员


2.故每个模板类有自己类模板的static数据成员的副本,即具有独有的static成员
 

相关标签: C 类模板