c++学习第十讲---类和对象---模版

发布时间:2024年01月18日

模版:

一.模版的概念:

建立通用的模具,提高复用性。

特点:模板不可以直接使用,它只是一个框架。

分类:函数模板和类模板。

二.函数模板:

1.函数模板语法:

(1)作用:

建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类? ? ? ? ? ? ? ? ? ? ? 型来代表。

(2)语法:
template<typename T>
//函数声明或定义

template --- 声明创建模板

typename --- 表示其后面的符号是一种数据类型,可以用class替代。

T --- 通用的数据类型,名称可以替换,通常为大写字母。

例:创建一个交换模板

template<typename T>
void Swap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
(3)使用:

有两种使用方式:自动类型推导 和 显示指定类型。

指定类型需在函数后面加上<数据类型>

int main()
{
	int a = 10;
	int b = 20;
	//利用函数模板交换
	//1.自动类型推导
	Swap(a, b);//编译器自动识别为int
	//2.显示指定类型
	Swap<int>(a, b);//指定T为int类型
	return 0;
}

2.函数模板注意事项:

1.自动类型推导,必须推导出一致的数据类型才可以使用。

2.模板必须确定 T 的数据类型才可以使用。

template<typename T>
void func()
{

}
func();//报错,无法确定T的类型
func<int>();

3.普通函数和模板函数的区别:

问:是否可以发生隐式类型转换?

答:普通函数,显示指定类型的模板 --- 可以
? ? ? ?自动类型推导的模板 --- 不可以

//普通函数
int Add(int a, int b)
{
	return a + b;
}
//函数模板
template<typename T>
T Add2(T a, T b)
{
	return a + b;
}
int main()
{
	int a = 10;
	int b = 20;
	char c = 'c';

	cout << Add(a, c) << endl;//109,将c转换为对应的ASSCII码值
	cout << Add2(a, c) << endl;//报错
	cout << Add2<int>(a, c) << endl;//不报错

	return 0;
}

4.普通函数和函数模板的调用规则:

调用规则:

1.普通和模板都可实现,优先普通
2.可通过空模板参数列表强制调用函数模板
3.函数模板可重载
4.如果函数模板可更好的匹配,优先函数模板

实例分析:

void Print(int a, int b)
{
	cout << "调用普通函数" << endl;
}
template<class T>
void Print(T a, T b)
{
	cout << "调用模板" << endl;
}

1.普通和模板都可实现,优先普通

Print(10,20);

2.可通过空模板参数列表强制调用函数模板

Print<>(10, 20);

3.函数模板可重载

template<class T>
void Print(T a, T b)
{
	cout << "调用模板" << endl;
}
template<class T>
void Print(T a)
{
	cout << "调用重载模板" << endl;
}

4.如果函数模板可更好的匹配,优先函数模板

Print('c','b');

普通函数也可以使用,需要隐式类型转换。

但是函数模板匹配,故优先模板。

5.模板的局限性:

如果函数模板的一些操作不适用于某些类型和自定义类型,那么模板就不适用了。

c++为解决这些问题,提供模板的重载,可以为特定类型提供具体模板。

例:

class Person
{
public:
	int age;
	Person(int a)
	{
		age = a;
	}
};
#include<stdbool.h>
//对比两个数据是否相等
template<class T>
bool Compare(T& a, T& b)
{
	if (a == b)//注意,这里在运行时会报错,因为两Person类无法比较
		return true;
	else return false;
}
int main()
{
	Person p1(10);
	Person p2(20);
	if (Compare(p1, p2))
	{
		cout << "相等" << endl;
	}
	else cout << "不相等" << endl;
	return 0;
}

这里两Person类无法比较相等,故会报错。

解决方法:

(1)运算符重载:略

(2)模板重载:

写一个Person类的重载函数,并在函数前加template<>表示是模板的特定重载函数。

template<>bool Compare(Person& a, Person& b)
{
	if (a.age == b.age)
		return true;
	else return false;
}

三.类模板:

1.类模板语法:

如果需要多个待定的数据类型,可在模板数据列表中写多个。

template<class NameType,class AgeType>
class Person
{
public:
	NameType m_Name;
	AgeType m_Age;
	Person(NameType n, AgeType a)
	{
		m_Name = n;
		m_Age = a;
	}
};

2.类模板和函数模板的区别:

(1)类模板没有自动类型推导,必须指定数据类型
(2)类模板在模板参数列表中可以有默认参数

实例分析:

(1)类模板没有自动类型推导,必须指定数据类型

int main()
{
	Person p("孙悟空",1000);//报错,没有参数列表
	Person<string, int> p("孙悟空",1000);
	return 0;
}

(2)类模板在模板参数列表中可以有默认参数

在template里可以指定默认数据类型

template<class NameType,class AgeType = int>

这样,在创建对象时,可以省略参数。

Person<string> p("孙悟空",1000);//省略了int

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

类模板中成员函数在调用时才创建。

例:

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};
class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}
};
template<class T>
class Class
{
public:
	T obj;
	//类模板中的成员函数
	void func1()
	{
		obj.showPerson1();//没有报错
	}
	void func2()
	{
		obj.showPerson2();//没有报错
	}
};

如上面的代码,在类模板中的成员函数并没有报错,但其实两个成员函数是不兼容的。

说明类模板中的成员函数在调用时才创建。

4.类模板对象作函数参数:

一共有三种传入方式:

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

实例分析:

1.指定传入的类型 --- 直接显示对象的数据类型:

//1.指定传入的类型-- - 直接显示对象的数据类型
void printPerson1(Person<string, int>& p)
{
	p.showPerson();
}
void test01()
{
	Person<string, int>p("张三", 500);
	printPerson1(p);
}

2.参数模板化 --- 将对象中的参数变为模板进行传递:

//2.参数模板化-- - 将对象中的参数变为模板进行传递
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
	p.showPerson();
}
void test02()
{
	Person<string, int>p2("李四", 40);
	printPerson2(p2);
}

3.整个类模板化 --- 将这个对象类型 模板化进行传递:

//3.整个类模板化-- - 将这个对象类型 模板化进行传递
template<class T>
void printPerson3(T &p)
{
	p.showPerson();
}
void test03()
{
	Person<string, int>p3("王五", 50);
	printPerson3(p3);
}

注:在实际应用中,第一种指定类型最常用。

5.类模板与继承:

父类是类模板时,子类 --- 具体化T,或者将子类也写成模板

先来一个模板父类:

template<class T>
class Base
{
public:
	T m;
};

(1)具体化T:

在父类后指定T的类型

class Son :public Base<int>
{

};

(2)将子类也写成模板:

模板参数T,在父类后参数列表写T。

注:如果将父类中继承的属性赋值,需加作用域(Base<T> : :)。

template<class T>
class Son2 :public Base<T>
{
public:
	Son2(T a)
	{
		Base<T>::m = a;
	}
};

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

比较普通成员函数类外实现:

(1)在函数上方加模板声明:template<class T1, class T2>
(2)作用域加参数列表:<T1,T2>

template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age);

	void showPerson()
	{
		cout << m_name << " " << m_age << endl;
	}
	T1 m_name;
	T2 m_age;
};
template<class T1, class T2>//模板声明
Person<T1,T2>::Person(T1 name, T2 age)//作用域加参数列表
{
	m_name = name;
	m_age = age;
}

7.类模板与友元:

设计一个全局函数展示成员信息:

(1)类内实现:

template<class T1, class T2>
class Person
{
public:
	friend void showPerson(Person<T1, T2> p)
	{
		cout << p.m_name << " " << p.m_age;
	}
	Person(T1 name, T2 age)
	{
		m_name = name;
		m_age = age;
	}
private:
	T1 m_name;
	T2 m_age;
};

(2)类外实现:(复杂,不推荐)

先将类声明,再将函数实现,最后实现类,并在类内声明友元。

template<class T1, class T2>
class Person;
template<class T1, class T2>
void showPerson(Person<T1, T2> p)
{
	cout << p.m_name << " " << p.m_age;
}
template<class T1, class T2>
class Person
{
public:
	friend void showPerson<>(Person<T1, T2> p);
	Person(T1 name, T2 age)
	{
		m_name = name;
		m_age = age;
	}
private:
	T1 m_name;
	T2 m_age;
};

文章来源:https://blog.csdn.net/2301_78922485/article/details/135537514
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。