C++ 提高编程

C++ 提高编程 针对的是泛型编程和STL技术做详细讲解,探讨C++更深层的使用

模板的概念
模板就是建立通用的模板,大大提高复用性.

模板的目的, 为了提高复用性, 将类型参数化

函数模板
template
void test_func(T &a, T &b)
// 声明一个模板, 告诉编译器后面代码中紧跟着的T不要报错, T是一个通用数据类型

template – 声明创建模板
typename – 表面其后面的符号是一种数据类型, 可以用class 代替
T – 通用的数据类型, 名称可以替代, 通常为大写字母

1, 自动类型推导
test_func(a, b), 编译器自动推导T的类型
2, 显示器指定类型

test_func<int>(a, b)

函数模板注意事项
注意事项:
自动类型推到, 必须推导出一致的数据类型T, 才可以使用
模板必须要确定出T的数据类型, 才可以使用

template<typename T>
void testfunc(T a, T B)
{

}

int a, b;
char c;
testfunc(a, b);    // 正确, 可以推导出一致的数据类型
testfunc(a, c);    // 不正确, 不能推导一致的数类型


template<typename T>
void testfunc2(T a)
{
}

testfunc2()  // 不正确, 因为不能确定T的数据类型,
testfunc2<int>() // 正确, 使用显示法, 可以确定T的数据类型


普通函数与函数模板的区别

普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时, 如果利用自动类型推导, 不会发生隐式类型转换
如果利用显示指定类型方式, 可以发生隐式类型转换.

template<typename T>
void testfunc3(T a, T, b);

int a, b;
char c;
testfunc(a, b);    // 正确, 可以推导出一致的数据类型
testfunc(a, c);    // 不正确, 不能推导一致的数类型(不能发生隐式类型转换)

testfunc<int>(a, c); // 正确, 这样可以将c进行隐式类型转换

普通函数与函数模板调用规则
1, 如果函数模板和普通函数都可以调用, 优先调用普通函数
2, 可以通过空模板参数列表, 强制调用, 函数模板
3, 函数模板可以发生函数重载
4, 如果函数模板可以产生更好的匹配, 优先调用函数模板(比如, 普通函数不要发生隐式转换)

testfunc<>(a, c);   // 通过空模板参数列表, 强制调用函数模板


template<typename T>
void testfunc(T a, T B)
template<typename T>
void testfunc(T a, T B, T C)     // 函数重载


void testfunc(int a, int b)

char a, b;
testfunc(a, b); // 优先调用函数模板, 因为自动推导出T是 char , 而普通函数还需要进行隐式转换,将int 转换成char 



利用具体化的模板, 可以解决自定义类型的通用化
学习模板并不是为了写模板, 而是在STL能够运用系统提供的模板

template<> testfunc(peson &p1, peson &p2)   // 使用函数模板的重载以及具体化的模板, 可以解决自定义的类型通用化
{

}

类模板使用只能用显示指定类型方式
类模板中的模板参数列表可以有默认参数

template<class NameType, class AgeType>
class Person
{
	person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	NameType m_Name;
	AgeType m_Age;
}

person p("猴子", 1000); // 错误, 类模型不能使用自动推导方式
person<string, int> p("猴子", 1000); // 正确, 只能使用显示指定类型方式

template<class NameType, class AgeType = int>  // 类模型可以使用默认参数

类模板中的成员函数并不是一开始就创建, 在调用时才会去创建.

类模板对象做函数参数
一共有三种传入方式
指定传入的类型, --直接显示对象的数据类型
参数模板化 – 将对象中的参数变为模板进行传递
整个类模板化 – 将这个对象类型模板进行传递.


template<class T1, class T2>
class Person
{
	person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	T1 m_Name;
	T2 m_Age;
}

1,指定传入类型
void testfunc(Person<string, int>&p)
2, 参数模板化
template<class T1, class T2>
void testfunc2(Person<T1, T2>&p)  // T1, T2会进行自动推导得出类型

3, 整个类模板化
template<class T>
void testfunc3(T &p)
查看编译器中的数据类型
typeid(T1).name()  // 可以通过 out <<  进行打印出来


类模板与继承
当类模板碰到继承时, 需要注意以下几点:
当子类继承的父类是一个类模板时, 子类在声明的时候,要指定出父类的T的类型
如果不指定, 编译器无法给子类分配内存
如果想灵活指定父类中的T的类型, 子类也需要变为模板

template(class T)
class Base
{
	T m;
}
template (class T1, class T2)
class son: public Base<T2>
{
T1 y;
}

son<int, char> S2;  // 这样就是将int 给了子类的T1, char 给了父类的T



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

template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 ate);
	void showPerson();
	T1 m_Name;
	T2 m_Age;
	
}

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 ate)  // 类外构造函数的实现
{

}
template<class T1, class T2>
void Person<T1, T2>::showPerson()  // 普通成员函数
{

}

Person<string , int> p1("龙哥", 100);
p1.showPerson(); // 调用普通成员函数

#pragma once // 可以防止头文件重复包含
一般将类模板(不管是声明还是实现, 都放在同一个文件), 然后将该文件重新命名后缀为.hpp,即别人知道该文件是一个类模板的文件,
然后其他文件调用该文件, 即进行include <xxx.hpp即可> . 注意:添加#pragma once 防止头文件被重复包含

类模板的友元
全局函数类内实现, 直接在类内声明友元即可
全局函数类外实现 需要提前让编译器知道全局函数的存在

template<class T1, class T2> class person;   // 这里是声明 目的让类外实现知道类的存在

template<class T1, class T2> void testfunc(person<T1,T2> & p); // 这个也是声明, 目的让编译器知道类中有引用了这个全局函数

template<class T1, class T2>
void testfunc(person<T1,T2> & p)      // 或者在类的前面进行对这个函数的实现
{
 
}

template<class T1, class T2>
 class person
 {
 	friend void testfunc1(person<T1, T2>p)
 	{
 		// 类内实现
 	}
	friend void testfunc<>(person<T1, T2>p);  // 使用空模板进行函数类模板的声明
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值