C++类模板

 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分离编写

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成员
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值