C++模板初阶小笔记

简介: C++模板初阶小笔记

目录

一.泛型编程

二.函数模板

1.函数模板语法梳理:

2.函数模板的实例化:

3.函数模板的显式实例化:

4.函数模板使用时的注意事项

三.类模板

1.类模板的语法梳理

2.类模板中声明和定义分离的成员函数

一.泛型编程
泛型编程:编写不依赖于具体数据类型的通用代码,是代码复用的一种手段。

二.函数模板
1.函数模板语法梳理:
C++中可以编写形参类型待定的函数模板。

假如我们现在要设计一个各种类型变量都通用的变量交换函数:

template
void swap(T1& Element1, T1& Element2)
{

T1 tem = Element1;
Element1 = Element2;
Element2 = tem;

}
函数模板书写格式:
template
返回值类型 函数名(形参列表){函数体}

template是定义模板的关键字
typename是定义模板待定变量类型的关键字
T1,T2...代表待定类型名,用于作为函数模板的形参类型名
注意函数模板本身并不是一个可调用的函数,由函数模板生成可调函数的过程是由编译器在编译阶段完成的
2.函数模板的实例化:
上面代码段中的swap就是一个函数模板,函数模板代表了一个函数家族,swap的形参Element1和Element2的类型都是待定的,实际调用swap函数时:

编译器会根据函数调用语句中传入函数的实参的类型生成对应的重载函数(编译器生成模板的重载函数的过程叫做函数模板的实例化),并调用重载函数。
也就是说,函数模板转变为可调函数的过程是在代码编译的阶段由编译器根据具体函数调用语句中实参的类型类完成的
比如:
template 定义函数模板
void swap(T1& Element1, T1& Element2)
{

T1 tem = Element1;
Element1 = Element2;
Element2 = tem;

}

class Date
{
public:

Date(char year = 0)
    :_year(year)
{
    cout << "constructor" << endl;
}

private:

char _year;

};

int main()
{

int a = 1;
int b = 2;
swap(a, b);                   交换一对整形变量

double d = 1.0;
double c = 2.0;
swap(d, c);                   交换一对浮点型变量

Date date1('b');
Date date2('c');
swap(date1, date2);           交换一对Date对象

return 0;

}

代码编译阶段编译器将函数模板转化为可调函数的过程图解:

这种函数模板的实例化方式称为隐式实例化(让编译器自己根据实参推演模板参数的实际类型),函数模板还可以显式实例化。
函数重载机制是支持函数模板化编程的底层机制
3.函数模板的显式实例化:
在函数调用语句的函数名后的<>中指定模板参数的实际类型。

template 定义函数模板
void swap(T1& Element1, T1& Element2)
{

T1 tem = Element1;
Element1 = Element2;
Element2 = tem;

}

int main ()
{

double d = 1.0;
double c = 2.0;
swap<double>(d, c);    
return 0;

}

4.函数模板使用时的注意事项
一个非模板函数可以和一个同名的函数模板同时存在。
// 专门处理int的加法函数
int Add(int left, int right)
{

return left + right;

}
// 通用加法函数
template
T Add(T left, T right)
{

return left + right;

}
void Test()
{

Add(1, 2);      //与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); //调用编译器特化的Add版本(显式实例化模板)

}

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。但是如果模板可以产生一个具有更好匹配的函数, 编译器将选择模板实例化函数。

// 专门处理int的加法函数
int Add(int left, int right)
{

return left + right;

}
// 通用加法函数
template
T1 Add(T1 left, T2 right)
{

return left + right;

}
void Test()
{

Add(1, 2);     // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0);   // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数

}

模板实例化函数不能自动对实参进行强制类型转换,但普通函数可以自动对实参进行强制类型转换

三.类模板
1.类模板的语法梳理
类模板的定义格式:

template //类模板的未定变量类型列表
class Date //类模板名
{

 T1 _mem1; //类中未定类型的成员的声明
 T2 _nem2;
 ....      

};
T1,T2...具体的类型名在创建类对象时由用户自行指定
比如现在定义一个栈类的模板:

class Date
{
public:

Date(char year = 0)
    :_year(year)
{
    cout << "constructor" << endl;
}

private:

char _year;

};

template //Stack类模板
class Stack
{
public:

Stack(int capacity = 4, int nums = 0)    //类的构造函数
    :_data = nullptr                     //构造函数初始化列表
    , _capacity = capacity
    , _nums = nums
{
    _data = new T[_capacity];             //为栈申请初始的堆区空间
}

~Stack()                                 //类的析构函数
{
    delete[] _data;                         //将栈申请的堆区空间还给系统
    _data = nullptr;
}

private:

T* _data;
int _capacity;
int _nums;

};

int main()
{

Stack<int> a;           //创建一个存储int类型数据的栈对象

Stack<double>b;            //创建一个存储double类型数据的栈对象

Stack<Date>c;           //创建一个存储Date对象的栈对象

return 0;

}

注意:

用类模板的方式定义的栈对象使用起来十分方便,可以用于存储各种类型的数据(一般来说实现一个栈类需要上百行代码,实现三种数据类型的栈就要三份代码,然而如果使用模板型编程方式,代码书写量就可以大大减少)

2.类模板中声明和定义分离的成员函数
如果类模板中的成员函数要放在类外进行定义时,需要加上模板参数列表,比如:

template
class Stack
{
public:

Stack(int capacity = 4, int nums = 0)    //类的构造函数
    :_data = nullptr                     //构造函数初始化列表
    , _capacity = capacity
    , _nums = nums
{
    _data = new T[_capacity];             //为栈申请初始的堆区空间
}

~Stack();     //类的析构函数的声明, 将析构函数的定义放在类模板外面

private:

T* _data;
int _capacity;
int _nums;

};

template
Stack::~Stack() //类的析构函数的定义
{

delete[] _data;
_data = nullptr;

}
模板型编程模式可以让代码变得更简洁,简洁的代码维护起来才更加的方便高效。

相关文章
|
3月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
121 0
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
89 0
|
6月前
|
编译器 C++
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
|
7月前
|
编译器 C++
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
7月前
|
安全 C++
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
98 0
|
7月前
|
存储 安全 算法
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
|
10月前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
329 4
|
10月前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
131 3
|
11月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
86 1
|
10月前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
122 0