(常识)C++中的模板(函数模板、类模板)——参数传递方式?继承与模板?分文件编写?
作者:求一个demo
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
内容通俗易懂,没有废话,文章最后是面试常问内容(建议通过标题目录学习)
废话不多说,我们直接开始------>>>>>>
一、函数模板
1、定义:生成通用函数。
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
cout << max(3, 5); // 隐式实例化
cout << max<double>(3.1, 5.2); // 显式实例化
}
2、特点:
①编译器根据调用时的参数类型自动推导参数。
②可以重载(不同的参数列表)。
③支持多个模板参数:template<typename T1,typename T2>。
④这样声明函数模板,只能传入类型相同的参数:template<typename T>。
⑤普通函数调用时可以发生自动类型转换。函数模板调用时,根据传入参数自动类型推导可隐式实例化,也可以显示实例化(如add<int>(a,b))。
二、类模板
1、定义:生成通用类。
template <typename T>
class Stack {
private:
vector<T> elements;
public:
void push(const T& element);
T pop();
};
int main() {
Stack<int> intStack;
Stack<string> stringStack;
}
2、特点:
①必须显式指定参数类型。
②可以有默认模板参数:template<typename T = int>。
③成员函数的实现通常写在头文件中(在.h文件中声明,在.h文件中写函数体。因为模板类和模板函数并非真正的类和函数,使用的时候用相应参数实例化即可)。
3、类模板和函数模板对比
①实例化方式:函数模板可隐式或显式实例化;类模板只能显式实例化。
②参数推导:函数模板支持自动类型推导;类模板不支持自动类型推导。
③重载:函数模板支持重载;类模板不支持重载(但可特化,当类模板在某些特定类型上需要有不同的行为/实现时,可以使用特化)。
// 普通的类模板
template <typename T>
class MyClass {
public:
void printType() {
std::cout << "Generic type" << std::endl;
}
};
// 类模板特化
template <>
class MyClass<int> {
public:
void printType() {
std::cout << "Integer type" << std::endl;
}
};
④默认参数:函数模板支持(C++11起);类模板支持。
三、参数传递方式
1、指定传入的类型
void printPerson1(Person<string, int>& p) // 1、指定传入类型
{
p.showPerson();
}
2、参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p) // 2、参数模板化
{
cout << "T的类型为: " << typeid(T2).name() << endl;
}
3、整个类模板化
template <class T>
void printPerson3(T& p) // 3、整个类模板化
{
cout << "T的类型为: " << typeid(T).name() << endl;
}
四、继承与模板
1、类模板继承普通类
class Base { /*...*/ };
template <typename T>
class Derived : public Base { /*...*/ };
2、类模板继承类模板
template <typename T>
class Base { /*...*/ };
template <typename T>
class Derived : public Base<T> { /*...*/ };
3、注意事项:
①派生类模板可以特化基类模板。
②在派生类中使用基类成员时,可能需要使用this->或Base<T>::前缀。
③当子类继承的父类是一个类模板时,在继承的时候,要指定出父类里模板的数据类型。如果不指定,编译器无法给子类分配内存(因为子类的内存布局包含从父类那继承过来的成员变量和成员函数,如果父类模板的参数类型未指定,则子类无法被分配内存)。
五、分文件编写
模板的声明和实现通常放在头文件中,因为:模板代码需要在使用时实例化;编译器需要看到完整的模板定义才能生成特定类型的代码。
1、都写在头文件中
// stack.h
template <typename T>
class Stack {
public:
void push(const T& element);
};
template <typename T>
void Stack<T>::push(const T& element) {
// 实现
}
2、分离式
// stack.h
template <typename T>
class Stack {
public:
void push(const T& element);
};
// stack_impl.h
#include "stack.h"
template <typename T>
void Stack<T>::push(const T& element) {
// 实现
}
3、显式实例化
// stack.cpp
#include "stack.h"
// 显式实例化常用类型
template class Stack<int>;
template class Stack<double>;
六、校招面试常问内容
1、什么是模板?为什么需要模板?
(1)模板:允许编写与类型无关的代码。
(2)为什么需要模板:减少重复代码、代码复用。
2、函数模板和类模板的区别?
①函数模板支持自动类型推导,类模板不支持自动类型推导。
②函数模板可以隐式和显式实例化,类模板只能显式实例化。
③函数模板可以重载,类模板不能重载但是可以特化。
3、函数模板如何实现重载?
template <typename T> void func(T a); // #1
template <typename T> void func(T* a); // #2
void func(int a); // #3
4、什么是模板特化?什么时候用?
(1)模板特化:当模板在某些特定类型上需要有不同的行为/实现时,可以特殊实现。
(2)
template <> void swap<Student>(Student& a, Student& b) {
// 针对Student的特殊交换逻辑
}
5、如何实现类模板的继承?
template <typename T>
class Base { /*...*/ };
template <typename T>
class Derived : public Base<T> { /*...*/ };
6、模板参数有哪些种类?
①类型参数:template<typename T>。
②非类型参数:template<int N>。
③模板模板参数:template<template<typename> class Container>。
7、为什么模板实现通常放在头文件中?
①模板需要在使用时实例化,编译器必须能看到完整定义。
②可以通过显式实例化来实现。
最后,如有不足和错误的地方,期待私信指正!