(十四)C++进阶之类模板(二)

1.1、类模板

类模板相对于函数模板而言的话相对复杂很多,因为要考虑到不同文件实现对象而导致模板之间定义问题。

类模板与函数模板的定义和使用类似,我们已经进行了介绍。 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,所以将类中的类型进行泛化。

格式如下:

在这里插入图片描述

我们简单写个例子:

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

using namespace std;

template <class T>
class Student
{
public:
	Student(T id, T height)
	{
		this->id = id;
		this->height = height;
	}

	void print(void)
	{
		cout << "id : " << id << " height:" << height << endl;
	}
private:
	T id;
	T height;
};

int main(void)
{
	Student<int> s(10,20);

	s.print();
	return 0;
}

运行结果:
在这里插入图片描述

这种使用方法和函数模版是一样的,我们在定义一个多个参数的。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

using namespace std;

template <class T1,class T2>
class Student
{
public:
	Student(T1 id, T2 name)
	{
		this->id = id;
		this->name = name;
	}

	void print(void)
	{
		cout << "id : " << id << " 名字:" << name << endl;
	}
private:
	T1 id;
	T2 name;
};

int main(void)
{
	Student<int,string> s(10,"张三");

	s.print();
	return 0;
}

运行结果:
在这里插入图片描述

1.2、类模板之派生类

当我们定义派生类的时候又该如何定义类模板呢?

如下面代码:

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

using namespace std;

template<class T>
class Person
{
private:
	T id;
	T age;
};

class child:public Person
{

};

int main(void)
{
	return 0;
}


但是编译器提示出错了:
在这里插入图片描述

我们当然能理解,毕竟父类中存在一个T是不明确的,所以我么可以这样尝试,在子类上面申明是类模板
在这里插入图片描述

虽然语法上过得去,但是还是编译出错,其实可以这么理解,子类上面的T是属于子类的,可是因为你继承父类中也是类模板,但是在父类中的T并没有明确类型,也就是编译器根本没办法根据这个类型来给成员开辟空间,所以在继承过程中,必须明确父类的模板类型。

所以我们可以这样修改:

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

using namespace std;

template<class T>
class Person
{
public:

	Person(T id, T age)
	{
		this->id = id;
		this->age = age;
	}

	T id;
	T age;
};

template<class T>
class child:public Person<int>
{
public:
	child(T id, T age, T height) :Person(id, age)
	{
		this->height = height;
	}

	void print(void)
	{
		cout << "id:" << this->id << "  age:" << this->age << "  height:" << this->height << endl;
	}

public:
	T height;
};

int main(void)
{

	child<int> c(1,2,3);

	c.print();

	return 0;
}

在继承过程中主要是明确父类的数据类型:

class child:public Person<int>

1.3、类模板之类内实现

所谓类的类内实现,主要是某些函数在类里面进行实现,比如我们重载+实现复数的加法。

#include <iostream>

using namespace std;

template<class T>
class Person
{
public:

	Person()
	{
		this->real = 0;
		this->imaginary = 0;
	}

	Person(T real, T imaginary)
	{
		this->real = real;
		this->imaginary = imaginary;
	}

	Person operator+(Person &other)
	{
		this->real = this->real + other.real;
		this->imaginary = this->imaginary + other.imaginary;

		return *this;
	}

	void print(void)
	{
		cout << this->real << " + " << this->imaginary << "i" << endl;
	}


private:
	T real;     //实数
	T imaginary;//虚数
};


int main(void)
{

	Person<int> p1(1,2);
	Person<int> p2(1, 2);

	Person<int> p3;
	
	p3 = p1 + p2;

	p3.print();

	return 0;
}

运行结果:
在这里插入图片描述

在类模板内实现函数和平常使用的方式几乎一样。

1.4、类模板之类外实现(同一个.cpp)

我们将上面那个的例子重载 +定义为友元函数,然后将构造、析构、打印函数在类的外面实现:


#include <iostream>

using namespace std;

template<class T>
class Person
{
public:

	friend  Person<T> operator+(Person<T> &p1, Person<T> &p2);

	Person();

	Person(T real, T imaginary);


	void print(void);


private:
	T real;     //实数
	T imaginary;//虚数
};

template<class T>
Person<T>::Person()
{
	this->real = 0;
	this->imaginary = 0;
}

template<class T>
Person<T>::Person(T real, T imaginary)
{
	this->real = real;
	this->imaginary = imaginary;
}

template<class T>
void Person<T>::print(void)
{
	cout << this->real << " + " << this->imaginary << "i" << endl;
}



template<class T>
Person<T> operator+(Person<T> &p1, Person<T> &p2)
{
	Person<T> temp;

	temp.real = p1.real + p2.real;
	temp.imaginary = p1.imaginary + p2.imaginary;

	return temp;
}


int main(void)
{

	Person<int> p1(1, 2);
	Person<int> p2(1, 2);

	Person<int> p3;

	p3 = p1 + p2;

	p3.print();

	return 0;
}

可是在编译完后发现提示一个错误:
在这里插入图片描述

其实就是无法解释友元函数的申明,那么我么加上一个模板定义看看。
在友元申明函数上面协商一个模板申明。
在这里插入图片描述

编译运行,最终结果:
在这里插入图片描述

虽然能够运行,但是你以为这种写法就是正确的么?

我们丢到linux下面去运行看看,如下图,已经在linux下编写完成:
在这里插入图片描述

进行编译后发现出错了:
在这里插入图片描述

也就是说不同编译器之间存在少许差异,这些差异我们暂且不谈(具体细节我也现在搞不清楚,暂时留下这个坑),我根据《轻松搞定C++》这本书籍上面描述暂且以VS2013为编译器进行描述:

1、在模板类中 如果有友元重载操作符<<或者>>需要在operator<< 和 参数列表之间加入 < T >。
2、如果说是不是<<和>> ,需要在在模板类中当友元函数在这个模板类 之前声明这个函数。

例如上面那个例子:

template<class T>
class Person;

template<class T>
Person<T> operator+(Person<T> &p1, Person<T> &p2);

在类前面对他进行申明。

运行结果:
在这里插入图片描述

其实我测试发现,哪怕不申明,只需要添加 < T >,也是可以运行的。
在这里插入图片描述

这里就有些混乱了,于是我去查看C++宝典,里面有这段话描述:
在这里插入图片描述

根据这段话的描述,其实就是我们第二点的描述,所以建议以后写类模板的友元函数一律按照这种方式来写,别偷懒。

我们折腾这么一大段,这个类模板的友元函数使用起来极其麻烦,不仅滥⽤用友元函数,而且本来可以当成员函数,却要⽤用友元函数。

所以在这里极其建议:模板类 不要轻易写友元函数, 要写的 就写<<
和>> 。

1.5、类模板之类外实现(一个.cpp,一个.h)

这里指的是创建一个类,进行实现函数。
我们创建一个类,具体创建方法不懂的可以百度。
在这里插入图片描述

这是我们的主函数:

#include <iostream>
#include "Person.h"

using namespace std;

int main(void)
{
	Person<int> p(1,2);
	p.print();
	return 0;
}

这是我们的类的.h文件:

#pragma once

template<class T>
class Person
{
public:
	Person(T id, T age);
	~Person();

	void print(void);

private:
	T id;
	T age;
};


这是类的.c文件

#include "Person.h"

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


template<class T>
Person<T>::~Person()
{
}



template<class T>
void Person<T>::print(void)
{
	cout << "id:" << this->id << "  age:" << this->age << endl;
}

其实我们的目的很简单,就是创建一个人的对象,然后将她的信息打印出来。
我们编译一下:
在这里插入图片描述

发现报错了,我解释一下这个错误的缘由。

之前我们讲函数模板的时候,说过模板会进行二次编译,第一次编译会检测语法是否过得去,第二次编译是根据实际的参数创建对应的函数。

而且我们也知道编译的过程:
在这里插入图片描述

编译器会单独对每个.c文件编译成目标文件,他里面的函数他会设定一个占位符,留着链接器根据所有的目标文件,进行替换。

结合上面这两点,我们就确定一下,语法上是没问题的,问题是出在链接器那里,为什么这么说呢?

因为类那个文件只是第一次编译成目标文件,因为你没有具体的参数,所以他不会生成具体的函数,也就是不会进行第二次编译。

而在主函数中这个占位符,链接器想去寻找这个函数的时候,发现并没有对应的函数(因为模板函数没有进行第二次编译),所以直接报错了。

那么怎么修改呢?

既然想要模板进行两次编译,只需要告诉他具体的类型参数进去即可,而且是在同一个文件内,因为你只要分开,他就单独编译了。

在主函数文件中,添加这个头文件:

#include "Person.cpp"

其实说白了,就是把类函数中定义的模板拿到和主函数一个文件内,那么在同一个文件,既有定义,又有真实传参,所以模板函数肯定会二次编译产生具体函数,这样就可以调用了。

运行结果如下:
在这里插入图片描述

其实为了区分,我们一般写专门定义类函数模板的地方的文件
在这里插入图片描述

这个后缀就代表他是类的函数模版,里面都是定义的模板,并没有具体实现,所以使用时候需要注意。

1.5、类模板之static

关于类模板和函数模板一个道理,就是一个模子生产实际事物,就是一个原型,生产实际函数。

那么关于static是否是类模板的共享资源,还是不同实现的类的共同资源呢?

毫无疑问,只有在第二次编译的时候才产生实际的类,利用类模板产生的类之间是不同的,所以对于里面的static成员,也是各自属于各自。

他们的关系如下面这个图:
在这里插入图片描述

发布了29 篇原创文章 · 获赞 0 · 访问量 407

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104507785