C++学习之类模板

简介: C++学习之类模板

C++ 模板的概念

C++模板是一种编程技术,使程序员能够编写通用的代码,在不同数据类型或数据结构上进行操作,而无需为每种情况都编写特定的代码。模板可以实现泛型编程,增加代码的重用性和灵活性。

在C++中,有两种主要类型的模板:函数模板和类模板。

C++ 类模板的基本语法

类模板(Class Template)是C++中的一种特殊类型,它允许在定义类时使用一个或多个类型作为参数,从而实现类型无关性和代码复用的目的。类模板的基本语法如下:

template <class T>
class ClassName {
    // 类成员和函数声明
};

其中:

  • template <class T>:使用template关键字开始类模板的定义,<class T>指定了类型参数的占位符名为T。也可以使用typename关键字代替class
  • ClassName:类名,可以根据需要自定义。

在类模板中,可以使用类型参数T作为成员变量的类型或函数的参数和返回类型。下面是一个示例:

template <class T>
class Stack {
private:
    T* elements;
    int top;
    int maxSize;
public:
    Stack(int size);
    void Push(T element);
    T Pop();
};
template <class T>
Stack<T>::Stack(int size) {
    maxSize = size;
    elements = new T[maxSize];
    top = -1;
}
template <class T>
void Stack<T>::Push(T element) {
    if (top == maxSize - 1) {
        cout << "Stack is full." << endl;
    } else {
        top++;
        elements[top] = element;
    }
}
template <class T>
T Stack<T>::Pop() {
    if (top == -1) {
        cout << "Stack is empty." << endl;
        return T();
    } else {
        T temp = elements[top];
        top--;
        return temp;
    }
}

以上示例定义了一个Stack类模板,类型参数为T。使用T* elements保存栈的元素,int top表示栈顶位置,maxSize保存最大容量。构造函数、入栈方法和出栈方法都使用了类型参数T作为参数或返回类型。

使用类模板时,可以通过指定具体类型来实例化该类。例如:

Stack<int> intStack(10);  // 实例化一个存储int类型元素的栈
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
intStack.Pop();  // 返回3

这样,就可以使用一个类模板来定义多种类型的栈。

C++ 类模板和函数模板区别

类模板(Class Template)和函数模板(Function Template)是C++中两种重要的模板,它们虽然都用于实现通用的代码,但在功能和用法上有一些区别。

  1. 定义目的:
  • 类模板用于定义一个通用的类,可以根据具体的类型参数生成不同的类,从而实现代码重用和类型无关性。
  • 函数模板用于定义一个通用的函数,可以根据实际参数的类型推导出函数模板的具体形式,实现对不同类型的数据进行相同的操作。
  1. 语法上的区别:
  • 类模板使用template <class T>template <typename T>开头,然后在类的定义中使用类型参数T
  • 函数模板使用template <class T>template <typename T>开头,然后在函数的定义中使用类型参数T
  1. 灵活性:
  • 类模板可以含有成员变量、成员函数等,因此可以实现更复杂的数据结构和算法。
  • 函数模板主要用于定义能够适用于多种类型的函数,提高代码的复用性和通用性。
  1. 使用方法:
  • 使用类模板时,需要在实例化时指定具体的类型,比如ClassName<int> obj;
  • 使用函数模板时,编译器可以自动推导出模板参数类型,也可以显式地指定类型,比如func<int>(5);

示例代码如下:

类模板示例:

template <class T>
class Pair {
private:
    T first;
    T second;
public:
    Pair(T f, T s) : first(f), second(s) {}
    T getFirst() { return first; }
    T getSecond() { return second; }
};
Pair<int> intPair(1, 2);
int firstValue = intPair.getFirst(); // 返回1
int secondValue = intPair.getSecond(); // 返回2

函数模板示例:

template <class T>
T max(T a, T b) {
    return (a > b) ? a : b;
}
int result1 = max(3, 5);  // 返回5,编译器自动推导类型为int
double result2 = max(4.5, 2.7);  // 返回4.5,编译器自动推导类型为double

总之,类模板用于定义通用的类,函数模板用于定义通用的函数,二者都有各自的用途和特点。

C++ 类模板中成员函数创建时机

在C++中,类模板中的成员函数并不是在类被声明时就立即创建的,而是在实例化类模板的过程中根据具体类型进行创建的。换句话说,类模板中的成员函数模板并不会被编译器实例化生成代码,直到使用该类模板创建对象时才会进行实例化。

当使用类模板创建对象时,编译器会根据实际需要对类模板中的成员函数进行实例化,生成特定类型的函数实现,并将其编译到目标文件中。这就意味着只有当使用类模板的实例(比如指定具体类型参数的对象)时,对应的成员函数实现才会被创建。

下面通过一个简单的示例说明类模板中成员函数的创建时机:

#include <iostream>
template <class T>
class MyClass {
public:
    void printType() {
        std::cout << "Type is: " << typeid(T).name() << std::endl;
    }
};
int main() {
    MyClass<int> myInt;
    MyClass<double> myDouble;
    myInt.printType();  // 实例化时创建对应类型的printType成员函数
    myDouble.printType(); // 实例化时创建对应类型的printType成员函数
    return 0;
}

在上面的示例中,MyClass是一个类模板,其中定义了一个成员函数printType。在main函数中,我们分别实例化了两个MyClass对象,分别为MyClass<int>MyClass<double>。当实例化对象时,编译器会根据<int><double>具体的类型参数来实例化对应的printType函数实现,因此在每次实例化时会根据具体的类型生成对应的函数。

C++ 类模板对象做函数参数

类模板的对象可以作为函数的参数传递。函数可以接受类模板对象作为参数,并在函数内部使用该对象执行相应的操作。

以下是一个示例,演示了如何将类模板的对象作为函数参数传递:

#include <iostream>
template <class T>
class MyClass {
private:
    T data;
public:
    MyClass(T d) : data(d) {}
    T getData() { return data; }
};
template <class T>
void printData(MyClass<T> obj) {
    T data = obj.getData();
    std::cout << "Data: " << data << std::endl;
}
int main() {
    MyClass<int> intObj(10);
    MyClass<double> doubleObj(3.14);
    printData(intObj);  // 使用int类型的对象调用printData函数
    printData(doubleObj); // 使用double类型的对象调用printData函数
    return 0;
}

在上述示例中,MyClass是一个类模板,其中包含了一个成员变量和一个成员函数。printData是一个函数模板,接受一个MyClass对象作为参数,并通过调用对象的成员函数获取数据并打印出来。

main函数中,我们分别创建了一个MyClass<int>类型的对象intObj和一个MyClass<double>类型的对象doubleObj。然后,我们将这两个对象分别作为参数调用了printData函数,实现了对不同类型的MyClass对象进行数据打印。

C++ 类模板与继承

类模板和继承是C++中的两种重要特性,它们可以结合在一起使用来实现更加灵活和强大的代码设计。

  1. 类模板
  • 类模板是一种通用的类定义,可以通过指定具体类型来创建特定的类实例。
  • 类模板可以包含成员变量、成员函数和静态成员等,这些成员可以使用模板参数作为类型或数值参数。
  • 类模板可以在编译时生成多个不同类型的类,从而实现代码重用和泛型编程的目的。
  1. 继承
  • 继承是面向对象编程中的重要概念,用于描述类之间的关系。一个类(子类)可以继承另一个类(父类)的成员函数和成员变量。
  • 子类会继承父类的属性和行为,并可以在此基础上进行扩展和定制化,实现代码的复用和扩展。
  • C++支持单继承和多继承,其中单继承是指一个类只能有一个直接基类,多继承是指一个类可以有多个直接基类。
  1. 类模板与继承的结合
  • 在C++中,类模板和继承可以结合使用,即可以定义一个继承自类模板的类,也可以将类模板的实例化作为基类来使用
  • 当一个类继承自类模板时,子类可以选择具体化父类的模板参数,也可以保留父类模板参数的通用性
  • 继承自类模板的子类可以进一步定制化和扩展基类模板的功能,提供更多特定类型的操作

类模板和继承都是C++中非常有用的特性,它们可以相辅相成,结合使用可以使代码更加灵活和高效。

C++ 类模板成员函数类外实现

在C++中,类模板的成员函数可以在类外进行实现。实现类模板的成员函数时,需要在函数名称前加上模板声明,并在函数定义之前使用template <typename T>(或template <class T>)指定模板参数。

下面是一个示例,演示了如何在类外实现类模板的成员函数:

#include <iostream>
template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T d) : data(d) {}
    void display();
};
// 类外实现成员函数
template <typename T>
void MyClass<T>::display() {
    std::cout << "Data: " << data << std::endl;
}
int main() {
    MyClass<int> intObj(10);
    MyClass<double> doubleObj(3.14);
    intObj.display();     // 输出:Data: 10
    doubleObj.display();  // 输出:Data: 3.14
    return 0;
}

在上述示例中,MyClass是一个类模板,里面包含一个数据成员data和成员函数 display。可以看到,display函数并没有在类内部定义,而是在类外部通过模板声明进行了定义。

在类外部定义类模板的成员函数时,需要确保模板参数与类模板声明中的参数符合,这样才能正确关联类模板和给定类型的成员函数实现。

C++ 类模板分文件编写

在C++中,可以将类模板的声明和定义分开写入不同的文件,并使用头文件进行包含。这样可以提高代码的可读性和维护性,方便在不同的文件中使用类模板。

下面是一个示例,演示了如何在不同的文件中编写类模板的声明和定义:

MyClass.h:

#ifndef MYCLASS_H
#define MYCLASS_H
template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T d);
    void display();
};
#include "MyClass.cpp"
#endif

MyClass.cpp:

#include <iostream>
#include "MyClass.h"
template <typename T>
MyClass<T>::MyClass(T d) : data(d) {}
template <typename T>
void MyClass<T>::display() {
    std::cout << "Data: " << data << std::endl;
}

main.cpp:

#include "MyClass.h"
int main() {
    MyClass<int> intObj(10);
    MyClass<double> doubleObj(3.14);
    intObj.display();     // 输出:Data: 10
    doubleObj.display();  // 输出:Data: 3.14
    return 0;
}

在这个示例中,MyClass类模板的声明被放置在名为"MyClass.h"的头文件中。该头文件包含了类模板的声明和MyClass.cpp文件的包含指令。注意,为了避免重复包含,我们使用了预处理指令#ifndef#define#endif来定义头文件的宏保护。

MyClass.cpp文件中,我们包含了"MyClass.h"头文件,并在其中编写了类模板的定义和成员函数的实现。

在主程序文件main.cpp中,我们只需包含头文件"MyClass.h"即可使用MyClass类模板以及其中的方法和成员。

通过将类模板的声明和定义分别放在不同的文件中,并进行头文件的包含,可以有效地进行类模板的分文件编写。这样可以提高代码的可读性和维护性,并使得类模板在各个文件中的使用更加方便。

C++ 类模板和友元

类模板中的友元关系与普通类的友元关系有些不同。在类模板中,可以为每个具体的模板实例指定自己的友元函数或友元类,而不是为整个类模板指定统一的友元。这使得友元关系更加灵活和可定制化。

以下是一个示例,演示了如何在类模板中定义友元函数和友元类:

#include <iostream>
template <typename T>
class MyClass {
private:
    T data;
public:
    MyClass(T d) : data(d) {}
    template <typename U>
    friend void displayData(const MyClass<U>& obj);
    template <typename U>
    friend class FriendClass;
};
template <typename T>
template <typename U>
void displayData(const MyClass<U>& obj) {
    std::cout << "Data: " << obj.data << std::endl;
}
template <typename T>
class FriendClass {
public:
    void displayData(const MyClass<T>& obj) {
        std::cout << "Data: " << obj.data << std::endl;
    }
};
int main() {
    MyClass<int> intObj(10);
    MyClass<double> doubleObj(3.14);
    displayData(intObj);          // 输出:Data: 10
    displayData(doubleObj);       // 输出:Data: 3.14
    FriendClass<int> friendObj;
    friendObj.displayData(intObj);  // 输出:Data: 10
    return 0;
}

在上述示例中,MyClass类模板中定义了一个私有数据成员data,以及一个友元函数模板displayData和一个友元类模板FriendClass

displayData函数模板是一个友元函数,可以访问MyClass模板的私有成员data。它的模板参数与MyClass模板的参数相互独立,这样可以为每个具体的模板实例单独定义不同的友元关系。

FriendClass类模板是一个友元类,可以访问MyClass模板的私有成员data。同样,它的模板参数与MyClass模板的参数相互独立。

main函数中,我们创建了MyClass<int>MyClass<double>的对象,并通过友元函数模板displayData和友元类模板FriendClass来访问其私有成员data

通过这种方式,我们可以为不同的具体模板实例创建不同的友元函数或友元类,以满足特定的需求。

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

相关文章
|
2天前
|
算法 数据处理 C++
|
2天前
|
编译器 C语言 C++
【C++】模板初阶(下)
C++的函数模板实例化分为隐式和显式。隐式实例化由编译器根据实参推断类型,如`Add(a1, a2)`,但`Add(a1, d1)`因类型不一致而失败。显式实例化如`Add&lt;double&gt;(a1, d1)`则直接指定类型。模板函数不支持自动类型转换,优先调用非模板函数。类模板类似,用于创建处理多种数据类型的类,如`Vector&lt;T&gt;`。实例化类模板如`Vector&lt;int&gt;`和`Vector&lt;double&gt;`创建具体类型对象。模板使用时,函数模板定义可分头文件和实现文件,但类模板通常全部放头文件以避免链接错误。
|
2天前
|
机器学习/深度学习 算法 编译器
【C++】模板初阶(上)
**C++模板简介** 探索C++泛型编程,通过模板提升代码复用。模板作为泛型编程基础,允许编写类型无关的通用代码。以`Swap`函数为例,传统方式需为每种类型编写单独函数,如`Swap(int&)`、`Swap(double&)`等,造成代码冗余。函数模板解决此问题,如`template&lt;typename T&gt; void Swap(T&, T&)`,编译器根据实参类型推导生成特定函数,减少重复代码,增强可维护性。模板分函数模板和类模板,提供处理不同数据类型但逻辑相似的功能。
|
2天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
2天前
|
算法 C++ 容器
|
2天前
|
存储 调度 C++
|
2天前
|
存储 安全 C++