函数模板和类模板 知识点总结 C++程序设计与算法笔记总结(七) 北京大学 郭炜(上)

简介: 函数模板和类模板 知识点总结 C++程序设计与算法笔记总结(七) 北京大学 郭炜(上)

函数模板

交换两个整型变量的值的Swap函数:
void Swap(int & x,int & y) 
{
int tmp = x;
x = y;
y = tmp;
}
交换两个double型变量的值的Swap函数:
void Swap(double & x,double & y) 
{
double tmp = x;
x = y;
y = tmp;
}

用函数模板解决:

用函数模板解决:
template <class 类型参数1,class 类型参数2,……>
返回值类型 模板名 (形参表)
{
函数体
};
template <class T>
void Swap(T & x,T & y) 
{
T tmp = x;
x = y;
y = tmp;
}
函数模板
int main()
{
int n = 1,m = 2;
Swap(n,m); //编译器自动生成 void Swap(int & ,int & )函数
double f = 1.2,g = 2.3;
Swap(f,g); //编译器自动生成 void Swap(double & ,double & )函数
return 0;
}
void Swap(double & x,double & y) 
{
double tmp = x;
x = y;
y = tmp

在C++中,函数模板是一种通用的函数定义,可以应用于不同的数据类型。它允许编写一次代码以适应多种不同的数据类型,实现代码的复用和泛化。

函数模板使用关键字 “template” 开始,并且后面跟着模板参数列表。模板参数列表可以包含一个或多个类型参数(如T、U等)或非类型参数(如整数常量)。例如:

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

上述代码中的函数模板 max 接受两个相同类型的参数,并返回较大的值。类型参数 T 可以是任何数据类型,比如整数、浮点数、字符等。

在实际调用函数模板时,编译器根据参数的类型将模板进行实例化,生成对应类型的函数。例如:

int result1 = max<int>(3, 5);      // 实例化为 max<int>, 返回 5
double result2 = max<double>(2.7, 1.5);  // 实例化为 max<double>, 返回 2.7
char result3 = max<char>('a', 'b');     // 实例化为 max<char>, 返回 'b'

在上面的例子中,通过 <类型> 的形式来指定实例化的具体类型,这样编译器就能够根据传入的类型生成对应的函数。如果没有显式指定类型,编译器会根据参数的类型自动推导出实例化的类型。

函数模板还可以有多个类型参数,并且可以有默认参数值。此外,你还可以在函数模板外定义非模板函数,它们可以与函数模板进行重载。

函数模板是C++中一种强大的工具,利用它可以编写通用且具有复用性的代码,可以处理不同类型的数据。

当需要在函数模板中处理多个不同类型的参数时,可以使用多个类型参数。

例如,下面是一个函数模板 swap,用于交换两个值:

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

上述代码中的 swap 函数模板接受两个相同类型的引用参数,并交换它们的值。使用该函数模板时,编译器将根据实参的类型实例化对应的函数。

int x = 5, y = 10;
swap(x, y);  // 实例化为 swap<int>(x, y),交换 x 和 y 的值
double a = 2.5, b = 3.7;
swap(a, b);  // 实例化为 swap<double>(a, b),交换 a 和 b 的值

除了类型参数,函数模板还可以包含非类型参数。非类型参数可以是整数、枚举、指针或引用类型,但不能是浮点数、类类型或 void 类型。

下面是一个示例,演示如何在函数模板中使用非类型参数来指定数组的大小:

template <typename T, int size>
void printArray(const T (&arr)[size]) {
    for (int i = 0; i < size; ++i) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

上述代码中的 printArray 函数模板接受一个固定大小的数组,并打印每个元素。通过将数组大小作为非类型参数传递给函数模板,可以在编译时知道数组的大小。

int intArray[] = {1, 2, 3, 4, 5};
printArray(intArray);  // 实例化为 printArray<int, 5>(intArray)
double doubleArray[] = {1.5, 2.7, 3.9};
printArray(doubleArray);  // 实例化为 printArray<double, 3>(doubleArray)

这样,函数模板就可以根据不同的数组大小生成对应的函数。

需要注意的是,在函数模板的定义和声明中,通常将模板参数放在尖括号 < > 中,并使用关键字 typenameclass 来声明类型参数。然而,你也可以使用非类型参数来调整模板的行为。

同时,函数模板还可以具有默认模板参数,以便更灵活地使用。默认模板参数允许指定某个或某些参数的默认值,使得在函数调用时可以省略掉这些参数。

C++中的函数模板是一种强大的工具,可以处理多个不同类型的参数,其中可以包含类型参数和非类型参数。通过使用函数模板,可以实现通用、可复用的代码,并根据实参的类型和值来自动生成对应的函数。

函数模板和函数的次序

在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句

  1. 先找参数完全匹配的普通函数(非由模板实例化而得的函数)。
  2. 再找参数完全匹配的模板函数。
  3. 再找实参数经过自动类型转换后能够匹配的普通函数。
  4. 上面的都找不到,则报错。

类模板

类模板 – 问题的提出

• 为了多快好省地定义出一批相似的类,可以定义类模板,然后由类模

板生成不同的类

• 数组是一种常见的数据类型,元素可以是:

– 整数

– 学生

– 字符串

– ……

• 考虑一个可变长数组类,需要提供的基本操作

– len():查看数组的长度

– getElement(int index):获取其中的一个元素

– setElement(int index):对其中的一个元素进行赋值

– ……

### 类模板的定义

template <typename 类型参数1,typename 类型参数2,……>
//类型参数表
class 类模板名
{
成员函数和成员变量
};
类模板里成员函数的写法:
template <class 类型参数1,class 类型参数2,……> //类型参数表
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{ 
……
}
用类模板定义对象的写法:
类模板名 <真实类型参数表> 对象名(构造函数实参表);
类模板示例: Pair类模板
template <class T1,class T2>
class Pair
{
public:
T1 key; //关键字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { };
bool operator < ( const Pair<T1,T2> & p) const; 
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < ( const Pair<T1,T2> & p) const 
//Pair的成员函数 operator <
{ 
return key < p.key; 
}

类模板示例:Pair类模板

int main()
{
Pair<string,int> student("Tom",19); 
//实例化出一个类 Pair<string,int>
cout << student.key << " " << student.value; 
return 0;
}
输出:
Tom 19

类模板(Class Template)是C++中另一种通用编程的工具,它允许定义一种通用的类,可以用于不同的数据类型。

类模板使用关键字 template 开始,并在尖括号 < > 中包含一个或多个类型参数。类型参数可以在类定义的内部作为类型的占位符使用。例如:

template <typename T>
class MyStack {
private:
    T* elements;
    int top;
    int capacity;
public:
    MyStack(int size) {
        elements = new T[size];
        capacity = size;
        top = -1;
    }
    // 其他成员函数的实现省略...
};

上述代码中的 MyStack 类模板定义了一个栈的数据结构,其中的元素类型 T 是一个占位符,在实际使用时会根据传入的类型进行实例化。

在实际使用类模板时,需要根据实际需求显式实例化特定类型的类。例如:

MyStack<int> intStack(10);       // 实例化为 MyStack<int>
MyStack<double> doubleStack(5);  // 实例化为 MyStack<double>

在上述代码中,分别创建了一个 int 类型和一个 double 类型的 MyStack 对象。每个对象都是根据相应的类型实例化的类。

类模板还可以有多个类型参数,并且可以包含非类型参数,与函数模板类似。可以通过提供特定类型的实参来显式指定实例化的类型,也可以让编译器自动推导类型。

类模板还可以有成员函数、成员变量、构造函数和析构函数等,可以根据具体需求进行实现。在定义类模板的成员函数时,可以像普通类一样使用 T 来引用模板参数所表示的类型。

template <typename T>
class MyStack {
    // ...
    void push(T element) {
        if (top + 1 >= capacity) {
            // 扩展容量代码...
        }
        elements[++top] = element;
    }
    T pop() {
        if (top < 0) {
            // 异常处理代码...
        }
        return elements[top--];
    }
    // ...
};

总结一下,类模板是C++中用于定义通用类的工具,允许创建可以适应不同数据类型的类。类模板使用关键字 template 和类型参数来定义,并通过显式实例化或类型推导来生成特定类型的类。类模板还可以包含成员函数、成员变量等,用于处理特定类型的数据。

用类模板定义对象

编译器由类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类,叫模板类。

同一个类模板的两个模板类是不兼容的

同一个类模板的两个模板类是不兼容的

是的,对于同一个类模板,不同的模板实例(即不同的模板参数)会生成不同的类型,这些类型在编译时是完全独立和不兼容的。

例如,考虑下面的类模板 MyStack

template <typename T>
class MyStack {
    // ...
};

使用 MyStack<int> 实例化的对象和使用 MyStack<double> 实例化的对象是完全不同的类型,它们之间是不兼容的。

MyStack<int> intStack;
MyStack<double> doubleStack;
intStack.push(5);
doubleStack.push(3.14);
int x = intStack.pop();         // 类型为 int
double y = doubleStack.pop();   // 类型为 double

在上述代码中,intStackdoubleStack 是两个完全不同的对象,它们的行为和类型都是根据实例化时的模板参数来确定的。

由于不同的模板实例生成的类型是不兼容的,因此不能将 MyStack<int> 的对象赋值给 MyStack<double> 的对象,也不能将它们混合使用。

MyStack<int> intStack;
MyStack<double> doubleStack;
// 以下代码是不允许的,会导致类型错误:
doubleStack = intStack;          // 错误:不兼容的类型
double x = intStack.pop();       // 错误:类型不匹配
intStack.push(3.14);             // 错误:类型不匹配

因此,对于同一个类模板生成的不同模板实例,它们是不兼容的,并且在使用时需要注意保持类型一致。

目录
相关文章
|
2月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
77 0
|
4月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
139 17
|
3月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
87 0
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
3月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
89 0
|
3月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
167 0
|
5月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
175 12
|
6月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
127 16
|
7月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
6月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。

热门文章

最新文章