【C++提高编程-02】----C++泛型编程之类模板实战

一伦明悦 2024-06-22 16:05:01 阅读 85

🎩 欢迎来到技术探索的奇幻世界👨‍💻

📜 个人主页:@一伦明悦-CSDN博客

✍🏻 作者简介: C++软件开发、Python机器学习爱好者

🗣️ 互动与支持💬评论      👍🏻点赞      📂收藏     👀关注+

如果文章有所帮助,欢迎留下您宝贵的评论,

点赞加收藏支持我,点击关注,一起进步!

前言             

        在C++中,泛型编程是一种编程范式,其核心思想是编写与数据类型无关的通用代码,以实现对不同数据类型的操作和算法。它主要利用的技术是模板。

        泛型编程的优点:

                代码复用: 泛型编程使得代码能够被多次使用,不需要为每种数据类型单独编写相似的代码。

                灵活性: 泛型编程可以适应不同类型的数据,使得代码更加灵活和通用。

                类型安全: 使用模板的泛型编程可以在编译期间进行类型检查,提高代码的安全性和可靠性。

正文 

01-类模板简介           

        在C++中,类模板是 C++ 中的一种特性,它允许我们编写通用的类,以便能够在不同数据类型上工作,从而实现代码的复用性和灵活性。类模板的定义以关键字 template 开始,然后是模板参数列表,接着是类的定义。

        下面是一个简单的类模板示例,实现了一个泛型的栈(stack)数据结构:在这个示例中,template <class T> 声明了一个类模板,T 是一个占位符,代表任意类型。在类定义中,我们使用了类型 T 来定义存储元素的数组和栈的操作。这样,我们可以创建不同类型的栈,如 Stack<int> 或 Stack<double>,以便通过模板实例化实现不同类型的数据处理。

template <class T>class Stack {private: T data[100]; int top;public: Stack() : top(-1) {} void push(const T& value) { if (top < 99) { data[++top] = value; } else { std::cout << "Stack is full!"; } } T pop() { if (top >= 0) { return data[top--]; } else { std::cout << "Stack is empty!"; return T(); // 返回 T 类型的默认值 } } bool isEmpty() { return top == -1; }};

        下面是一个使用上述类模板的示例代码:在这个示例中,我们使用 Stack<int> 实例化类模板,并将整型数据依次压入栈中,然后弹出并打印出栈内的元素。通过类模板,我们可以方便地实现通用的数据结构和算法,以适用于不同的数据类型。

int main() { Stack<int> intStack; intStack.push(10); intStack.push(20); intStack.push(30); while (!intStack.isEmpty()) { std::cout << intStack.pop() << " "; } return 0;}

        下面给出具体代码分析应用过程:

        这段代码定义了一个类模板 Person,其中包含两个模板参数 NameType 和 AgeType,分别表示姓名和年龄的数据类型。在类模板内部,定义了一个构造函数和一个 show 方法,用于初始化成员变量并展示姓名和年龄信息。

        在 test01 函数中,我们实例化了一个 Person<string, int> 类型的对象 p1,姓名为 “Tom”,年龄为 18,并调用其 show 方法展示信息。

        在 main 函数中,调用了 test01 函数来测试类模板的使用,并最后通过 system("pause") 暂停程序运行。

#include <iostream>using namespace std;#include <string>// 类模板template<class NameType,class AgeType> // 这里NameType和AgeType就像T一样,定义了一个模板式的类型class Person{public:Person(NameType name, AgeType age){this->m_Name = name; // 这里写this是为方便寻找成员变量,不写也可this->m_Age = age;}void show(){cout << "name=" << m_Name << "age = " << m_Age << endl;}NameType m_Name;AgeType m_Age;};void test01(){Person<string, int>p1("Tom", 18);p1.show();}int main(){test01();system("pause");return 0;}

        示例运行结果如下图所示:

02-类模板和函数模板区别           

        类模板和函数模板是 C++ 中两种不同的模板形式,它们分别用于定义通用的类和函数。下面详细解释类模板和函数模板之间的区别:

         定义方式:类模板使用 template <class T> 或 template <typename T> 的语法来定义模板类,其中 T 是类模板的占位符类型。函数模板使用 template <class T> 或 template <typename T> 的语法来定义模板函数,其中 T 是函数模板的占位符类型。

         作用范围:类模板可以用于定义通用的类,其中类中的数据成员和成员函数可以使用模板参数。函数模板可以用于定义通用的函数,其中函数的参数类型和返回值类型可以使用模板参数。

         实例化:类模板在实例化时需要指定模板参数的具体类型,例如 Stack<int> 或 Person<string, int>。函数模板在调用时可以根据实参的类型推断出模板参数的具体类型,也可以显式指定类型,例如 func<int>(value)

         模板参数位置:类模板的模板参数可以出现在类的任意位置,用于定义类中的成员变量、成员函数等。函数模板的模板参数出现在函数名称之前,用于定义函数的参数类型和返回值类型。

         灵活性:类模板的灵活性更高,可以用于定义通用的数据结构和算法,实现更复杂的逻辑。函数模板通常用于定义通用的函数算法,更适合处理函数逻辑上的通用性需求。总体来说,类模板和函数模板都是 C++ 提供的用于实现通用编程的重要特性,通过模板编程,可以提高代码的复用性和灵活性,同时减少代码的重复编写。选择类模板还是函数模板取决于具体的需求和场景,它们分别适用于不同的抽象层次和代

下面我们来分别实现一个类模板和一个函数模板,然后进行具体的代码分析:

首先是类模板的代码示例:

#include <iostream>using namespace std;// 类模板template <class T>class Pair {private: T first; T second;public: Pair(T f, T s) : first(f), second(s) {} void display() { cout << "Pair: " << first << ", " << second << endl; }};int main() { Pair<int> intPair(10, 20); Pair<string> strPair("Hello", "World"); intPair.display(); strPair.display(); return 0;}

接下来是函数模板的代码示例:

#include <iostream>using namespace std;// 函数模板template <class T>T maximum(T a, T b) { return (a > b) ? a : b;}int main() { int num1 = 10, num2 = 20; double double1 = 3.14, double2 = 2.71; cout << "Maximum of integers: " << maximum(num1, num2) << endl; cout << "Maximum of doubles: " << maximum(double1, double2) << endl; return 0;}

        在上面的代码中,我们定义了一个类模板 Pair 和一个函数模板 maximum。类模板 Pair 表示一对值,它包含两个模板参数,可以用不同类型的数据来实例化。函数模板 maximum 接受两个参数,并返回它们中较大的值。

        在 main 函数中,我们分别创建了两个类模板实例 intPair 和 strPair,以及通过函数模板调用找到了不同类型数据的最大值。

        通过这两个例子,我们可以看到类模板和函数模板的使用方式和特点。类模板适用于定义通用的类,而函数模板适用于定义通用的函数。它们都能通过模板参数实现对不同数据类型的操作,提高代码的复用性和灵活性。

        下面给出具体代码分析应用过程:

#include <iostream>using namespace std;#include <string>// 类模板和函数模板区别template<class NameType, class AgeType> // 这里NameType和AgeType就像T一样,定义了一个模板式的类型class Person{public:Person(NameType name, AgeType age){this->m_Name = name; // 这里写this是为方便寻找成员变量,不写也可this->m_Age = age;}void show(){cout << "name=" << m_Name << "age = " << m_Age << endl;}NameType m_Name;AgeType m_Age;};void test01(){Person<string, int>p1("Tom", 18);p1.show();}/* 1. 类模板没有自动类型推导的使用方式 必须要有显示数据类型Person<string, int> 2. 类模板在模板参数列表中可以有默认参数*/int main(){test01();system("pause");return 0;}

         示例运行结果如下图所示:

03-类模板中成员函数创建时机           

        在类模板中,成员函数的创建时机是在类被实例化时。当我们实例化一个类模板的对象时,编译器会根据具体的模板参数类型生成对应的类定义及其成员函数的实现,这意味着每个具体类型的模板参数都会对应一个特定版本的类模板及其中的成员函数。

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

        在下面的代码中,我们定义了一个类模板 MyClass,包含一个模板参数 T 和成员函数 display,用于显示保存的数据。在 main 函数中,我们实例化了两个不同类型的 MyClass 对象:intObj 是整型类型,strObj 是字符串类型。

        当编译器遇到 MyClass<int> 和 MyClass<string> 两个具体的实例化时,会创建两个不同的类定义,并为每个实例化生成相应的成员函数实现。因此,成员函数的创建时机是在类模板被实例化时,根据具体的模板参数类型生成对应的函数定义。

        这种特性使得类模板可以根据不同类型的需求生成不同的类定义和成员函数实现,实现了通用性和灵活性。

#include <iostream>#include <string>using namespace std;// 类模板template <class T>class MyClass {public: MyClass(T value) : data(value) {} void display() { cout << "Value: " << data << endl; }private: T data;};int main() { MyClass<int> intObj(10); MyClass<string> strObj("Hello"); intObj.display(); strObj.display(); return 0;}

 04-类模板对象做函数参数           

       类模板对象可以作为函数参数,通过这种方式可以实现对不同类型的类模板对象进行处理,从而提高代码的复用性和灵活性。当类模板对象作为函数参数传递时,需要指明模板参数类型,以确保编译器能够正确生成相应的函数实现。

/*1. 指定传入的类型 --- 直接显示对象的数据类型

2. 参数模板化 --- 将对象中的参数变为模板进行传递

3. 整个类模板化 --- 将这个对象类型 模板化进行传递

*/

        下面是一个简单的代码示例,演示了如何在函数中接受类模板对象作为参数:

        在这段代码中,我们定义了一个类模板 Box,包含一个模板参数 T 和一个成员函数 getValue 返回存储的值。然后,我们定义了一个函数 displayValue,接受一个类型为 Box<T> 的类模板对象作为参数,并在函数内部展示对象中的值。

        在 main 函数中,我们分别创建了两个不同类型的 Box 对象 intBox 和 strBox,然后将它们作为参数传递给 displayValue 函数进行展示。

        通过这种方式,我们可以实现对不同类型的类模板对象进行统一的处理,增强了代码的通用性和灵活性。

#include <iostream>#include <string>using namespace std;// 类模板template <class T>class Box {public: Box(T val) : value(val) {} T getValue() { return value; }private: T value;};// 函数接受类模板对象作为参数template <class T>void displayValue(Box<T> box) { cout << "Value in the box: " << box.getValue() << endl;}int main() { Box<int> intBox(10); Box<string> strBox("Hello"); displayValue(intBox); displayValue(strBox); return 0;}

05-类模板与继承          

       类模板和继承在 C++ 中可以结合使用,以实现对模板的进一步扩展和特化。通过继承,可以在子类中继承父类模板的通用特性,并在此基础上进行一些特定的功能扩展或修改。

        以下是一个示例代码,演示了类模板与继承的结合应用:

        在下面的代码中,我们定义了一个类模板 Box,表示一个简单的箱子,包含一个存储值的成员。然后,我们定义了一个派生类 SpecialBox,通过继承自 Box<T>,它包含了一个额外的特殊值 specialValue。在 displaySpecialValue 函数中,我们展示了特殊值的内容。

        在 main 函数中,我们分别实例化了 SpecialBox<int> 和 SpecialBox<string> 对象,并展示了其基类继承来的值,并调用了派生类的特有函数 displaySpecialValue

        通过类模板和继承的结合使用,我们可以实现对模板类的进一步特化和定制,使代码更具灵活性和扩展性。

#include <iostream>using namespace std;// 类模板template <class T>class Box {public: Box(T val) : value(val) {} T getValue() { return value; }private: T value;};// 派生类继承自类模板template <class T>class SpecialBox : public Box<T> {public: SpecialBox(T val, T specialVal) : Box<T>(val), specialValue(specialVal) {} void displaySpecialValue() { cout << "Special Value in the Special Box: " << specialValue << endl; }private: T specialValue;};int main() { SpecialBox<int> specialIntBox(10, 100); SpecialBox<string> specialStrBox("Hello", "Special"); cout << "Value in the specialIntBox: " << specialIntBox.getValue() << endl; specialIntBox.displaySpecialValue(); cout << "Value in the specialStrBox: " << specialStrBox.getValue() << endl; specialStrBox.displaySpecialValue(); return 0;}

 06-类模板成员函数的类外实现       

       在 C++ 中,类模板的成员函数通常可以直接在类定义内部进行实现,也可以在类外部进行实现。如果希望将类模板的成员函数的定义从类模板的定义中分离出来,可以在类外部进行实现。

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

        在下面的代码中,我们定义了一个类模板 Calculator,包含两个模板参数,分别代表运算数字的类型。我们在类外部实现了 add 和 subtract 两个成员函数,分别用于加法和减法运算。

        在函数实现时,我们使用了类似 template <class T> 的语法来指定实现的是 class template Calculator 中的成员函数。这样可以将成员函数的定义与类模板的定义分开,提高代码的可读性和维护性。

        在 main 函数中,我们分别实例化了 Calculator<int> 和 Calculator<double> 对象,并调用了类模板的成员函数进行加法和减法运算。

        通过将类模板的成员函数的实现分离到类外部,我们可以更灵活地管理类模板的实现细节,并提高代码的可维护性。

#include <iostream>using namespace std;// 类模板的声明template <class T>class Calculator {public: Calculator(T num1, T num2) : number1(num1), number2(num2) {} T add(); T subtract();};// 类外部实现类模板的成员函数template <class T>T Calculator<T>::add() { return number1 + number2;}template <class T>T Calculator<T>::subtract() { return number1 - number2;}int main() { Calculator<int> intCalc(10, 5); Calculator<double> doubleCalc(3.5, 2.1); cout << "Adding: " << intCalc.add() << endl; cout << "Subtracting: " << doubleCalc.subtract() << endl; return 0;}

        下面给出具体代码分析应用过程:这段代码展示了一个类模板 Person,其中包含一个构造函数和一个成员函数 show,并且这两个函数的实现被分别放在类的外部。

        在类模板 Person 中,模板参数分别为 T1 和 T2,用来表示 m_Name 和 m_Age 的数据类型。构造函数 Person(T1 name, T2 age) 接受一个名字和一个年龄作为参数,并将其赋值给类中的成员变量 m_Name 和 m_Age。而成员函数 show() 用于展示对象的名字和年龄。

        在类的外部,通过 Person<T1, T2>::Person(T1 name, T2 age) 的方式实现了构造函数,将参数赋值给类的成员变量。同样,在类外部实现了成员函数 Person<T1, T2>::show(),用来显示对象的名字和年龄。

        在 main 函数中,通过实例化 Person<string, int> 创建了一个 Person 对象 p,传入名字 “Tom” 和年龄 18。然后调用 p.show() 函数展示 p 对象的名字和年龄。

        通过类模板和类外实现的结合,可以更好地实现代码的重用和维护,提高了代码的可读性和灵活性。

#include<iostream>using namespace std;#include<string>// 类模板的成员函数的类外实现template<class T1 ,class T2>class Person{public: // 这里必须对成员函数声明时必须给出权限设置,因为默认时私有的,外部无法访问Person(T1 name, T2 age);// {// this->m_Name = name;// this->m_Age = age;// // }void show();// {// cout << "a" << m_Name << "b" << m_Age << endl;// }// 现在无论构造函数还是成员函数都是在类内实现,需要在类外实现T1 m_Name;T2 m_Age;};// 类外实现构造函数 这个以后编写代码都是在头文件和.cpp文件中,用处很大template<class T1,class T2>Person<T1, T2>::Person(T1 name, T2 age) // Person<T1, T2>这句可以看作时类模板的成员函数的类外实现{ {this->m_Name = name;this->m_Age = age;}}// 成员函数类外实现template<class T1, class T2>void Person<T1, T2>::show(){cout << "a" << m_Name << "b" << m_Age << endl;}void test01(){Person<string, int>p("Tom", 18);p.show();}int main() {test01();system("pause");return 0;}

        示例运行结果如下图所示: 

总结             

      类模板是 C++ 中一种强大的特性,允许我们编写通用的类,以便用不同的数据类型进行实例化。以下是类模板的总结要点:

        定义类模板:使用 template <class T> 或 template <typename T> 来定义类模板,其中 T 是类型参数,可以在类的定义中使用该参数来表示不特定的数据类型。例如 template <class T> class MyTemplate { /* 类定义 */ };

        实例化类模板:通过指定实际的数据类型,可以实例化类模板为具体的类,例如 MyTemplate<int> obj1; MyTemplate<double> obj2;

        类模板成员函数:类模板可以包含成员函数,这些成员函数也可以是模板函数,可以在类的内部定义或外部定义。

        类模板特化:可以针对特定的数据类型制定特殊的实现,从而实现类模板的特化。例如 template <> class MyTemplate<char> { /* 特化实现 */ };

        多个模板参数:类模板可以有多个模板参数,例如 template <class T, class U> class Pair { /* 类定义 */ };

        模板模板参数:模板还可以作为另一个模板的参数,称为模板模板参数,在实现更复杂的类模板时非常有用。

        使用限制:类模板不能将成员变量直接声明为 static,每个类模板的实例化都是独立的,具有相同的成员函数但独立的成员变量。

        通过使用类模板,我们可以编写出更加通用、灵活和可重用的类,以更好地适应不同数据类型的需求。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。