【C++ 程序设计】第 9 章:函数模板与类模板

目录

一、函数模板

(1)函数模板的概念

(2)函数模板的示例 

(3)函数或函数模板调用语句的匹配顺序 

二、类模板

(1)类模板概念 

(2)类模板示例 

(3)类模板与继承  




一、函数模板

  • 设计程序中的函数时,可能会遇到函数中参数的类型有差异,但需要实现的功能类似的情形。
  • 函数重载可以处理这种情形。
  • 重载函数的参数表中,可以写不同类型的参数,从而可以处理不同的情形。

(1)函数模板的概念

  • 为了提高效率,实现代码复用,C++ 提供了一种处理机制,即使用函数模板
  • 函数在设计时并不使用实际的类型,而是使用虚拟的类型参数。
  • 这样可以不必为每种不同的类型都编写代码段。
  • 当用实际的类型来实例化这种函数时,将函数模板与某个具体数据类型连用。
  • 编译器将以函数模板为样板,生成一个函数,即产生了模板函数,这个过程称为函数模板实例化。
  • 函数模板实例化的过程由编译器完成。
  • 程序设计时并不给出相应数据的类型,编译时,由编译器根据实际的类型进行实例化。

(2)函数模板的示例 

  • 实际上,函数模板不是一个具体的函数,编译器不能为其生成可执行代码。
  • 定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
  • 函数模板中还可以带多个类型参数。
虽然 函数模板 的使用形式与 函数 类似,但二者有本质的 区别 ,主要表现在以下 3 个方面:
  • 函数模板本身在编译时不会生成任何目标代码,只有当通过模板生成具体的函数实例时才会生成目标代码。
  • 被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。
  • 函数指针也只能指向模板的实例,而不能指向模板本身。

【示例一】 创建了一个模板函数 abs,通过模板参数 T,实现获取任意类型变量的绝对值。在主函数中定义了不同类型的变量,并通过 abs 函数输出它们的绝对值

扫描二维码关注公众号,回复: 15593821 查看本文章

【示例代码】

#include <iostream> // 导入 iostream 库
using namespace std;

template<typename T> // 创建模板函数
T abs(T x) // 定义模板函数
{
    return x < 0 ? -x : x; // 使用三目运算符计算绝对值并返回
}

int main() // 主函数
{
    int n = -5; // 初始化变量 n 为 -5
    int m = 10; // 初始化变量 m 为 10
    double d = -0.5; // 初始化变量 d 为 -0.5
    float f = 3.2; // 初始化变量 f 为 3.2

    cout << n << "的绝对值是:" << abs(n) << endl; // 输出 n 的绝对值
    cout << m << "的绝对值是:" << abs(m) << endl; // 输出 m 的绝对值
    cout << d << "的绝对值是:" << abs(d) << endl; // 输出 d 的绝对值
    cout << f << "的绝对值是:" << abs(f) << endl; // 输出 f 的绝对值

    return 0; // 返回 0 表示执行成功
}

【代码详解】

  • 程序首先导入了 iostream 库,该库包含了 std::cout 和 std::endl 等标准输出工具。
  • 在模板函数 abs 中,我们使用了一个条件三目运算符返回传入参数的绝对值。该函数的参数是一个通用类型 T 变量 x。
  • 在主函数中,我们初始化了四个变量 nmd 和 f,分别为整型 -5、整型 10、双精度浮点型 -0.5 以及单精度浮点型 3.2。接下来我们分别调用了 abs 函数,输出各变量的绝对值。最后程序执行成功,返回 0

【执行结果】

  • 这段代码创建了一个模板函数 abs,通过模板参数 T,实现获取任意类型变量的绝对值。在主函数中定义了不同类型的变量,并通过 abs 函数输出它们的绝对值。
  • 这说明模板函数在多种类型的变量中都能正常工作。
  • 输出结果为:
    -5的绝对值是:5
    10的绝对值是:10
    -0.5的绝对值是:0.5
    3.2的绝对值是:3.2

【示例二】 模板函数

【示例代码】在主函数中,调用 abs(n) 时,编译器根据实参 n 的类型 int,推导出模板中的类型参数 T 为 int,然后实例化函数模板,生成函数模板 abs 的一个实例。

int abs(int x) //定义求整数x的绝对值函数
{
    return x < 0 ? -x : x; //判断x是否小于0,如果是则取相反数,否则返回x
}

【代码详解】

  • 这段代码定义了一个求整数绝对值的函数 abs,即传入整数 x,返回其对应的绝对值。用三目运算符来进行判断,如果 x 小于 0,则取相反数,否则直接返回 x

【示例三】 模板函数

【示例代码】当调用abs(d)时,根据实参d的类型double,又实例化一个新的函数。

double abs(double x) //定义求双精度浮点型x的绝对值函数
{
    return x < 0 ? -x : x; //判断x是否小于0,如果是则取相反数,否则返回x
}

【代码详解】

  • 这段代码定义了一个求双精度浮点数绝对值的函数 abs,即传入双精度浮点数 x,返回其对应的绝对值。用三目运算符来进行判断,如果 x 小于 0,则取相反数,否则直接返回 x
  • 实际上,函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模
    板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。

【示例四】通用的函数模板和一个日期类,其中函数模板可交换不同类型的变量值,并测试了模板函数 Swap 在不同数据类型下的使用

【示例代码】

#include<iostream> //包含标准输入输出流库
using namespace std;

template<class T> //定义通用类型模板
void Swap(T &x, T &y) //定义模板函数 Swap,参数为 T 类型引用 x 和 y
{
    T tmp = x; //定义 T 类型变量 tmp,赋值为 x
    x = y; //把 y 的值赋给 x
    y = tmp; //把 tmp 的值赋给 y
}

class myDate //定义 myDate 类
{
public:
    myDate(); //默认构造函数
    myDate(int, int, int); //构造函数
    void printDate() const; //打印日期信息
private:
    int year, month, day; //成员变量:年、月、日
};

myDate::myDate() //默认构造函数的实现
{
    year = 1970; //初始年为 1970
    month = 1; //初始月为 1
    day = 1; //初始日为 1
}

myDate::myDate(int y, int m, int d) //参数构造函数的实现
{ 
    year = y; //初始化年份
    month = m; //初始化月份
    day = d; //初始化日期
}

void myDate::printDate() const //打印日期信息
{ 
    cout << year << "/" << month << "/" << day << endl; //输出日期
    return; //返回空值
}

int main() //主函数
{
    int n = 1, m = 2; //定义两个整型变量 n 和 m,分别赋值为 1 和 2
    Swap(n, m); //调用 Swap 函数进行值交换
    cout << n << " " << m << endl; //输出交换后的结果

    double f = 1.2, g = 2.3; //定义两个双精度浮点数变量 f 和 g,分别赋值为 1.2 和 2.3
    Swap(f, g); //调用 Swap 函数进行值交换
    cout << f << " " << g << endl; //输出交换后的结果

    myDate d1, d2(2000, 1, 1); //定义两个日期对象,d1 默认构造函数创建,d2 利用参数构造函数创建
    Swap(d1, d2); //调用 Swap 函数进行值交换
    d1.printDate(); //输出日期信息
    d2.printDate(); //输出日期信息

    return 0; //程序执行成功,返回0
}

【代码详解】

  • 该程序定义了一个通用类型模板 Swap,并利用此模板实现了 C++ 中的值交换,其中 Swap 可以实现不同类型的变量值交换。还定义了一个 myDate 类表示日期,包含构造函数、默认构造函数和打印日期信息的函数。在 main 函数中创建不同的类型变量,调用 Swap 函数进行值交换,以示此函数的通用性。
  • #include<iostream>:包含标准输入输出流库。
  • using namespace std;:使用标准库的命名空间。
  • template<class T>:定义一个通用类型模板,使用 class 或 typename 其中之一来声明类型模板参数,冒号后面的代码是模板变量的类型名称,即使用模板时实际传入的数据类型。
  • void Swap(T &x, T &y):定义模板函数 Swap,参数为 T 类型引用 x 和 y,函数的功能是交换 x 和 y 的值。
  • T tmp = x;:定义一个类型为 T 的 tmp 变量,并将 x 的值赋给它。
  • x = y;:将 y 的值赋给 x
  • y = tmp;:将 tmp 的值赋给 y
  • class myDate:定义了一个日期类 myDate
  • myDate():是默认构造函数,将年月日初始化为 1970 年 1 月 1 日。成员函数 myDate() 用于给成员变量赋初值,不需要写返回类型,因为在调用该函数时不需要返回任何值。
  • myDate(int y, int m, int d):是带参数的构造函数,用于初始化年月日的值。初始化时需要传入年、月、日三个整数值,根据值初始化成员变量。
  • void printDate() const:是打印日期信息的函数,成员函数 printDate() 用于输出日期类中三个数据的值,不会对成员变量进行修改,故加上 const 修饰符来防止在函数中修改成员变量。
  • int main():是主函数,是程序的执行起点。
  • int n = 1, m = 2;:定义两个整型变量 n 和 m,并将它们分别赋值为 1 和 2
  • Swap(n, m);:调用 Swap 函数,进行 n 和 m 的值交换。
  • cout << n << " " << m << endl;:输出交换后的 n 和 m
  • double f = 1.2, g = 2.3;:定义两个双精度浮点数变量 f 和 g,并将它们分别赋值为 1.2 和 2.3
  • Swap(f, g);:调用 Swap 函数,进行 f 和 g 的值交换。
  • cout << f << " " << g << endl;:输出交换后的 f 和 g
  • myDate d1, d2(2000, 1, 1);:定义两个日期对象,其中 d1 通过默认构造函数创建,d2 利用参数构造函数创建。
  • Swap(d1, d2);:调用 Swap 函数,进行 d1 和 d2 的值交换。
  • d1.printDate();:输出 d1 的日期信息。
  • d2.printDate();:输出 d2 的日期信息。
  • return 0;:程序执行成功,返回 0

【执行结果】

  • 首先,在主函数中分别定义了两个整型变量 n 和 m,并赋值为 1 和 2,然后调用了 Swap(n, m) 进行值的交换,并输出了交换后的结果,即 “1 2” 变成了 “2 1”。
  • 接下来,定义了两个双精度浮点数变量 f 和 g,并赋值为 1.2 和 2.3。调用了 Swap(f, g) 进行值的交换,并输出了交换后的结果,即 “1.2 2.3” 变成了 “2.3 1.2”。
  • 最后,定义了两个日期对象 d1 和 d2,d1 利用默认构造函数创建,d2 利用参数构造函数创建,然后调用了 Swap(d1, d2) 进行值的交换,也就是将 d1 和 d2 中的年、月、日都互相交换。最后分别输出了交换后的日期信息,即输出了 “1970/1/1” 和 “2000/1/1”。
  • 该程序的执行结果为:
1 2
2.3 1.2
1970/1/1
2000/1/1

【示例五】 函数模板中还可以带多个类型参数

【示例代码】下面这个函数模板的写法是合法的:

template <class T1, class T2> // 声明模板,T1和T2是类型参数
void print(T1 arg1, T2 arg2) // 定义打印函数,参数类型为T1和T2
{
    cout << arg1 << "," << arg2 << endl; // 打印参数arg1和arg2
}

【代码详解】

  • 此处定义了一个打印函数,其参数类型和数量不确定,由模板类型参数T1和T2决定。由于T1或T2可以为任何类型(包括内置类型和自定义类型),所以该函数在需要打印不同类型数据的时候就非常的方便。

  • 在编译时,编译器将根据函数调用时传递的参数类型,自动实例化生成特定类型的函数代码。并通过模板的显式实例化的方式可以减少编译时间和优化代码。

  • 该函数实现方式比较简单,直接将参数arg1和arg2输出到屏幕上,并在每次输出结束后换行。需要注意的是,cout为标准输出流对象,需要包含<iostream>头文件,否则编译会出错。


(3)函数或函数模板调用语句的匹配顺序 

函数与函数模板也是允许重载的。 

C++ 编译器遵循以下先后顺序:
  1. 先找参数完全匹配的普通函数(不是由模板实例化得到的模板函数)。
  2. 再找参数完全匹配的模板函数。
  3. 然后找实参经过自动类型转换后能够匹配的普通函数。
  4. 如果上面的都找不到,则报错。


二、类模板

(1)类模板概念 

  • 通过类模板,可以实例化一个个的类。
  • 继承机制也是在一系列的类之间建立某种联系,这两种涉及多个类的机制是有很大差异的。
  • 类是相同类型事物的抽象,有继承关系的类可以具有不同的操作。
  • 而模板是不同类型的事物具有相同的操作,实例化后的类之间没有联系,相互独立。

【格式一】声明类模板的一般格式如下:

template <模板参数表>
class 类模板名
{
    类体定义
}

【说明】

  • 其中,“模板参数表” 的形式与函数模板中的 “模板参数表” 完全一样。
  • 类体定义与普通类的定义几乎相同,只是在它的成员变量和成员函数中通常要用到模板的类型参数。

【格式二】

  • 类模板的成员函数既可以在类体内进行说明,也可以在类体外进行说明。
  • 如果在类体内定义,则自动成为内联函数。如果需要在类模板以外定义其成员函数,则要采用以下格式:
template <模板参数表>
返回类型名 类模板名<模板参数标识符列表>::成员函数名(参数表)
{
    函数体
}

【说明】

  • 类模板声明本身并不是一个类,它说明了类的一个家族。
  • 只有当被其他代码引用时,模板才根据引用的需要生成具体的类
【格式三】
  • 不能使用类模板来直接生成对象,因为类型参数是不确定的,必须先为模板参数指定 “实参”,即模板要 “实例化” 后,才可以创建对象。
  • 也就是说,当使用类模板创建对象时,要随类模板名给出对应于类型形参或普通形参的具体实参,格式如下:
类模板名 <模板参数表> 对象名1,…,对象名n;
// 或是
类模板名 <模板参数表> 对象名1(构造函数实参),…,对象名构造函数实参);
  • 编译器由类模板生成类的过程称为类模板的实例化。
  • 由类模板实例化得到的类称为模板类。
  • 要注意的是,与类型形参相对应的实参是类型名。

(2)类模板示例 

  • 二元组是常用的一种结构。
  • 可以定义两个值的二元组,如平面坐标系下点的横、纵坐标组成的二元组。
  • 还可以定义两个字符串的二元组,如字典中单词与释义组成的二元组。
  • 还可以定义学生姓名及其成绩的二元组。二元组的例子非常多,不胜枚举。
  • 如果要定义二元组的类,则需要根据组成二元组的类型定义很多不同的类。
  • 现在可以使用类模板来解决问题。

【示例一】 实现了模板类TestClass的定义和实例化

【示例代码】在 main 函数中依次创建 TestClass<char> 和 TestClass<double> 两个对象,并演示了如何使用它们的成员变量和成员函数:

#include <iostream> //包含标准输入输出流库
using namespace std;

template<class T> //定义一个模板类,其数据成员为T类型数组
class TestClass 
{
public:
    T buffer[10]; //T类型数组

    T getData(int j); //成员函数声明,用于获取该数组中的元素
};

template<class T> //定义模板类的成员函数,返回指定下标处的数据
T TestClass<T>::getData(int j)
{
    return *(buffer+j);
};

int main() //主函数
{ 
    TestClass<char> ClassInstA; //定义一个模板类的对象,类型是char
    int i;
    char cArr[6] = "abcde"; //定义一个char类型的数据数组
    for (i = 0; i < 5; i++) //赋值给buffer数组
        ClassInstA.buffer[i] = cArr[i];
    for (i = 0; i < 5; i++) //依次输出buffer中元素的值
    { 
        char res = ClassInstA.getData(i); //调用成员函数getData
        cout << res << " "; //打印结果
    }
    cout << endl; //换行

    TestClass<double> ClassInstF; //定义一个模板类的对象,类型是double
    double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6}; //定义一个double类型的数据数组
    for (i = 0; i < 6; i++) //赋值给buffer数组
        ClassInstF.buffer[i] = fArr[i] - 10;
    for (i = 0; i < 6; i++) //依次输出buffer中元素的值
    { 
        double res = ClassInstF.getData(i); //调用成员函数getData
        cout << res << " "; //打印结果
    }
    cout << endl; //换行

    return 0; //程序执行成功,返回0
}

【代码详解】

  • 定义了一个模板类 TestClass,其中包含一个 T 类型的数组 buffer 和一个成员函数getData,用于获取数组 buffer 中制定位置的元素:
    #include<iostream> //包含标准输入输出流库
    using namespace std;
    
    template<class T> //定义一个模板类,其数据成员为T类型数组
    class TestClass 
    {
    public:
        T buffer[10]; //T类型数组
    
        T getData(int j); //成员函数声明,用于获取该数组中的元素
    };
  • 在类外部定义函数 getData,使用作用域解析符 “::” 表示这个函数是属于 TestClass 类范围的。获取数组 buffer 中指定下标处的元素值:
    template<class T> //定义模板类的成员函数,返回指定下标处的数据
    T TestClass<T>::getData(int j)
    {
        return *(buffer+j);
    };
  • 在 main 函数中,首先定义一个 TestClass 对象 ClassInstA,类型被指定为 char。然后将数组 cArr 中的元素逐个赋值给 ClassInstA 的 buffer 数组,再依次调用成员函数getData 获取 buffer 中每个元素的值,并打印输出。执行完此段代码后输出的结果是:“a b c d e”(以空格隔开),最后换行:
    int main()
    { 
        TestClass<char> ClassInstA; //定义一个模板类的对象,类型是char
        int i;
        char cArr[6] = "abcde"; //定义一个char类型的数据数组
        for (i = 0; i < 5; i++) //赋值给buffer数组
            ClassInstA.buffer[i] = cArr[i];
        for (i = 0; i < 5; i++) //依次输出buffer中元素的值
        { 
            char res = ClassInstA.getData(i); //调用成员函数getData
            cout << res << " "; //打印结果
        }
        cout << endl; //换行
  • 接着,定义一个TestClass对象ClassInstF,类型被指定为double。然后将数组fArr中的元素减去10并依次存储在ClassInstF的buffer数组中,并逐个调用成员函数getData获取buffer中每个元素的值,并打印输出。执行完此段代码后输出的结果是:“2.1 13.2 24.3 35.4 46.5 57.6”(以空格隔开),最后换行:
        TestClass<double> ClassInstF; //定义一个模板类的对象,类型是double
        double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6}; //定义一个double类型的数据数组
        for (i = 0; i < 6; i++) //赋值给buffer数组
            ClassInstF.buffer[i] = fArr[i] - 10;
        for (i = 0; i < 6; i++) //依次输出buffer中元素的值
        { 
            double res = ClassInstF.getData(i); //调用成员函数getData
            cout << res << " "; //打印结果
        }
        cout << endl; //换行
  • 最后,在main函数中返回0,程序执行结束:
        return 0; //程序执行成功,返回0
    }

【执行结果】

  • 程序定义了一个模板类 TestClass ,其中有一个数据成员 buffer ,是一个 T 类型的数组。TestClass 还有一个成员函数 getData,用于获取 buffer 中指定下标处的值。
  • 在主函数中,首先创建了一个 TestClass<char> 类型的对象 ClassInstA,并将 "abcde" 这个字符串逐个存储在 ClassInstA 的 buffer 数组中,然后逐个获取 buffer 中元素的值,并打印输出。因此,执行结果中先打印出了 "abcde" 的每个字符(以空格隔开),然后换行。
  • 接着,创建了一个 TestClass<double> 类型的对象 ClassInstF,并将 {12.1, 23.2, 34.3, 45.4, 56.5, 67.6} 数组中的每个元素减去了 10 后,存储在 buffer 数组中。再依次获取 buffer 中元素的值,并打印输出。因此,执行结果中先打印出了每个元素减 10 后的值(以空格隔开),然后换行。
  • 需要注意的是,T 可以是任何数据类型,通过指定不同类型的类型参数 T,就可以创建出不同的类对象,并对这些对象进行操作。另外,因为是模板类,所以可以在程序中复用这个类模板创建很多对象,而不用针对每种需求都再编写一个新的类。
  • 该程序执行结果为:
a b c d e 
2.1 13.2 24.3 35.4 46.5 57.6 

【示例二】 类模板和数组的使用

【示例代码】展示了 C++ 中的类模板和数组的使用,以及如何通过数组的下标来访问数组中的元素,并输出其值:

#include <iostream>
using namespace std;

template<int i>
class TestClass {
public:
    int buffer[i]; // 声明一个大小固定为 i 的整型数组
    int getData(int j); // 声明一个获取 buffer 下标为 j 的元素值的方法
};

template<int i>
int TestClass<i>::getData(int j) {
    return *(buffer+j); // 返回 buffer 数组下标为 j 的元素值
}

int main() {
    TestClass<6> ClassInstF; // 初始化大小为 6 的 TestClass 实例
    int i;
    double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6};
    for (i = 0; i < 6; i++) {
        ClassInstF.buffer[i] = fArr[i] - 10; // 循环将 fArr 中每个元素减去 10 后赋值给 ClassInstF 的 buffer 数组
    }
    for (i = 0; i < 6; i++) {
        double res = ClassInstF.getData(i); // 循环获取 ClassInstF 的 buffer 数组中每个元素的值
        cout << res << " ";
    }
    cout << endl;
    return 0;
}

【代码详解】

  • 该程序定义了一个名为 TestClass 的类模板,该模板包含一个大小为 i 的整型数组和一个获取数组元素方法。继而在 main 函数中,首先声明了一个大小为 6 的 TestClass 类型的对象 ClassInstF,然后使用 for 循环将一个已知的双精度数组 fArr 中的每个元素减去 10 赋值给 ClassInstF 的 buffer 数组,最后再使用 for 循环访问 ClassInstF 的 buffer 数组,并输出其中每个元素的值。该程序可以帮助理解 C++ 中类模板和数组的使用方法。
  • 头文件声明和命名空间:
    #include <iostream>
    using namespace std;
  • 定义一个名为 TestClass 的类模板,模板参数为 i,其中 buffer 是一个大小为 i 的 int 类型数组,getData 是一个公共方法,参数为 int 类型的 j,返回值是 buffer 数组下标为 j 的元素值:
    template<int i>
    class TestClass {
    public:
        int buffer[i]; // 声明一个大小固定为 i 的整型数组
        int getData(int j); // 声明一个获取 buffer 下标为 j 的元素值的方法
    };
  • 定义 TestClass 类中 getData 方法的具体实现,根据传入的参数 j 对 buffer 数组进行下标访问,返回所得元素值:
    template<int i>
    int TestClass<i>::getData(int j) {
        return *(buffer+j); // 返回 buffer 数组下标为 j 的元素值
    }
  • 在 main 函数中,声明 TestClass<6> 类型的 ClassInstF 实例,其中 i 的值为 6。通过 for 循环,将大小为 6 的 fArr 数组中的每个元素值减去 10 后存入 ClassInstF 的 buffer 数组中。再次通过 for 循环,将 ClassInstF 的 buffer 数组中的每个元素值存入 res,然后输出该值。最后,输出一个换行符并返回 0:
    int main() {
        TestClass<6> ClassInstF; // 初始化大小为 6 的 TestClass 实例
        int i;
        double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6};
        for (i = 0; i < 6; i++) {
            ClassInstF.buffer[i] = fArr[i] - 10; // 循环将 fArr 中每个元素减去 10 后赋值给 ClassInstF 的 buffer 数组
        }
        for (i = 0; i < 6; i++) {
            double res = ClassInstF.getData(i); // 循环获取 ClassInstF 的 buffer 数组中每个元素的值
            cout << res << " ";
        }
        cout << endl;
        return 0;
    }

【执行结果】

  • 该结果是将 fArr 中的每个元素减去 10 后存储到 ClassInstF 的 buffer 数组中,然后依次输出 buffer 数组中的每个元素值。由于 buffer 数组的元素类型为整型,而 fArr 中的元素类型为双精度浮点数,因此在将 fArr 中的每个元素减去 10 时也会将其类型转换为整型。
2.1 13.2 24.3 35.4 46.5 57.6

(3)类模板与继承  

类之间允许继承,类模板之间也允许继承。
具体来说,类模板和类模板之间、类模板和类之间可以互相继承,它们之间的常见派生关系有以下 4 种情况:
  • 普通类继承模板类。
  • 类模板继承普通类。
  • 类模板继承类模板。
  • 类模板继承模板类。
根据类模板实例化的类即是模板类。

【示例一】 类模板的使用方法以及派生类的定义和继承的方法

【示例代码】

  • 以下程序定义了一个类模板基类 TBase 和一个从 TBase 派生而来的类 Derived,使用了类模板中的数据成员 data 和成员函数 print 输出数据成员 data 的值。
  • 在 main 函数中,程序首先创建了 Derived 类的一个对象 dDerived 继承了 TBase<int>,而 TBase 模板类中的数据类型是 T,类似于一个泛型,因此 data 的数据类型为 int。接着,程序调用 d 的成员函数 print(),输出了数据成员 data 的值到标准输出流中,即输出到屏幕上。
  • 以下程序主要是为了演示类模板的使用方法以及派生类的定义和继承的方法:
#include <iostream>
using namespace std;

template <class T> // 定义类模板基类
class TBase {
    T data; // 类模板基类中的数据成员
public:
    void print() { // 在类模板基类中定义成员函数
        cout << data << endl;
    }
};

class Derived : public TBase<int> { // 使用类模板基类,定义普通派生类
};

int main() {
    Derived d; // 定义普通派生类对象
    d.print(); // 调用类模板基类中的成员函数
    return 0;
}

【代码详解】

  • 该程序定义了名为 TBase 的类模板基类,这个类模板中包含一个数据成员 data 和一个成员函数 print,该成员函数用于输出 data 的值。接着,定义了名为 Derived 的普通派生类,该普通派生类是从模板基类 TBase 继承而来。在 main 函数中定义了一个 Derived 类的对象 d,然后调用了基类中的成员函数 print() 输出 data。由于 TBase 是一个类模板,因此使用时需要指定模板参数的类型,这里使用了 int
  • 包含头文件和命名空间的声明:
    #include <iostream>
    using namespace std;
  • 定义名为 TBase 的类模板,该模板中包含一个数据成员 data 和一个成员函数 print,函数 print 可以输出 data 的值:
    template <class T> // 定义类模板基类
    class TBase {
        T data; // 类模板基类中的数据成员
    public:
        void print() { // 在类模板基类中定义成员函数
            cout << data << endl;
        }
    };
  • 定义普通派生类 Derived,该类继承自 TBase<int>
    class Derived : public TBase<int> { // 使用类模板基类,定义普通派生类
    };
  • 在 main 函数中,首先声明一个名为 d 的派生类对象 Derived。然后,调用 d 的成员函数 print,输出 data 的值。由于 Derived 派生自 TBase<int>,因此 TBase 类模板中 data 的类型为 int,可以直接输出。最后返回值 0:
    int main() {
        Derived d; // 定义普通派生类对象
        d.print(); // 调用类模板基类中的成员函数
        return 0;
    }

【执行结果】

  • 因为 TBase 模板类中的数据成员 data 没有被初始化,其值将取决于存储在该内存位置上的旧数据。在该程序中,data 的类型为 int,因此默认初始值为 0。

  • 在 main 函数中,定义了一个名为 d 的 Derived 类对象,然后调用 d 继承自 TBase<int> 的成员函数 print,其输出的是 TBase 模板类中 data 的初始值 0。

  • 因此,程序输出了一个值为 0 的整数:

0

【示例二】 类的继承和成员函数的显式调用方法

【示例代码】

#include <iostream>
#include <string>

using namespace std;

class TBase { // 定义类 TBase
    int k; // 私有数据成员 k
public:
    void print() { // 公有成员函数 print 输出 k 的值
        cout << "TBase::" << k << endl;
    }
};

template <class T>
class TDerived : public TBase { // 定义类 TDerived 继承自 TBase
    T data; // 私有数据成员 data
public:
    void setData(T x) { // 设置 data 值
        data = x;
    }
    void print() { // 输出 data 和 k 的值
        TBase::print(); // 调用基类中的 print 函数
        cout << "TDerived::" << data << endl;
    }
};

int main() {
    TDerived<string> d; // 定义 TDerived 类对象 d
    d.setData("2019"); // 设置 d 的 data 值为 "2019"
    d.print(); // 输出 data 和 k 的值
    return 0;
}

【代码详解】

  • 该程序主要是为了演示类的继承和成员函数的显式调用方法,以及使用类模板。其中,基类 TBase 和派生类 TDerived 都包含了 print 函数,而在派生类中,需要使用 TBase::print() 显式调用基类的 print 函数才能输出基类中的 k

  • 包含头文件和命名空间的声明:
    #include <iostream>
    #include <string>
    
    using namespace std;
  • 定义了一个名为 TBase 的类,其中包含一个私有数据成员 k 和一个公有成员函数 printprint 函数输出 k 的值:
    class TBase { // 定义类 TBase
        int k; // 私有数据成员 k
    public:
        void print() { // 公有成员函数 print 输出 k 的值
            cout << "TBase::" << k << endl;
        }
    };
  • 定义一个类模板 TDerived,该类继承自 TBase,包含一个私有数据成员 data 和两个成员函数 setData 和 print。其中,setData 函数用于设置 data 的值,print 函数用于输出 data 和继承自基类 TBase 的 k 的值。需要注意的是,在 print 函数中,使用了 TBase::print() 对基类中的 print 函数进行了显式调用:
    template <class T>
    class TDerived : public TBase { // 定义类 TDerived 继承自 TBase
        T data; // 私有数据成员 data
    public:
        void setData(T x) { // 设置 data 值
            data = x;
        }
        void print() { // 输出 data 和 k 的值
            TBase::print(); // 调用基类中的 print 函数
            cout << "TDerived::" << data << endl;
        }
    };
  • 在 main 函数中,首先定义了一个名为 d 的 TDerived<string> 类对象,然后调用它的 setData 函数将 data 的值设置为字符串 “2019”。最后,调用 d 的 print 函数输出 data 和继承自基类 TBase 的 k 的值:
    int main() {
        TDerived<string> d; // 定义 TDerived 类对象 d
        d.setData("2019"); // 设置 d 的 data 值为 "2019"
        d.print(); // 输出 data 和 k 的值
        return 0;
    }

【执行结果】

  • 该程序中定义了一个基类 TBase 和一个继承自 TBase 的模板类 TDerived,其中 TBase 类包含一个私有数据成员 k,而 TDerived 类包含一个私有的数据成员 data,同时还包含了两个成员函数 setData 和 print。在 print 函数中,调用了基类 TBase 的 print 函数打印出 k 的值,并输出 data 的值到标准输出流中,即输出到屏幕上。
  • 在 main 函数中,首先定义了一个名为 d 的 TDerived<string> 类对象,然后调用它的 setData 函数将 data 的值设置为字符串 “2019”。最后,调用 d 的 print 函数输出 data 和继承自基类 TBase 的 k 的值。
  • 由于 TBase 类中的 k 数据成员没有显式初始化,在实例化 TDerived 类对象时,其数据成员 k 获得了默认值 0。因此,程序的输出结果为 “TBase::0\nTDerived::2019”。
TBase::0
TDerived::2019

【示例三】 类模板的定义、继承及其成员函数的调用

【示例代码】成员函数 print 的作用是输出类中数据成员的值到标准输出流,而且此程序实现了二次派生的类模板,并通过实例化不同类型的类模板来调用它们的 print 函数:

#include <iostream>
#include <string>

using namespace std;

template <class T>
class TBase { // 定义类模板 TBase
    T data1; // 私有数据成员 data1
public:
    void print() { // 成员函数 print 输出 data1 的值
        cout << "TBase::" << data1 << endl;
    }
};

template <class T1, class T2>
class TDerived : public TBase<T1> { // 定义类模板 TDerived,继承 TBase
    T2 data2; // 私有数据成员 data2
public:
    void print() { // 成员函数 print 输出 data1 和 data2 的值
        TBase<T1>::print(); // 调用基类 TBase 的 print 函数
        cout << "TDerived::" << data2 << endl;
    }
};

int main() {
    TDerived<int, int> d; // 声明 TDerived<int, int> 类对象 d
    d.print(); // 输出 data1 和 data2 的值
    TDerived<string, string> d2; // 声明 TDerived<string, string> 类对象 d2
    d2.print(); // 输出 data1 和 data2 的值
    return 0;
}

【代码详解】

  • 该程序主要是为了演示类模板的定义、继承及其成员函数的调用。成员函数 print 的作用是输出类中数据成员的值到标准输出流,程序通过实例化不同类型的类模板 TDerived 来调用它们的 print 函数。其中,基类 TBase 的 data1 和派生类 TDerived 的 data2 均被输出到标准输出流中。
  • 头文件声明和命名空间:
    #include <iostream>
    #include <string>
    
    using namespace std;
  • 定义了一个名为 TBase 的类模板,其中包含一个私有数据成员 data1 和一个公有成员函数 printprint 函数用于输出 data1 的值:
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template <class T>
    class TBase { // 定义类模板 TBase
        T data1; // 私有数据成员 data1
    public:
        void print() { // 成员函数 print 输出 data1 的值
            cout << "TBase::" << data1 << endl;
        }
    };
  • 定义了一个名为 TDerived 的类模板,继承自 TBase,并包含了一个私有数据成员 data2 和一个公有成员函数 print,其中在 print 函数中使用 TBase<T1>::print() 的形式进行了基类的显式调用,用于输出基类 TBase 中的 data1 的值,然后将 data2 的值输出到标准输出流中:
    template <class T1, class T2>
    class TDerived : public TBase<T1> { // 定义类模板 TDerived,继承 TBase
        T2 data2; // 私有数据成员 data2
    public:
        void print() { // 成员函数 print 输出 data1 和 data2 的值
            TBase<T1>::print(); // 调用基类 TBase 的 print 函数
            cout << "TDerived::" << data2 << endl;
        }
    };
  • 在 main 函数中,首先声明了一个 TDerived<int, int> 类对象 d ,并调用其 print 函数输出数据成员的值。接着声明了一个 TDerived<string, string> 类对象 d2 ,并调用其 print 函数输出数据成员的值:
    int main() {
        TDerived<int, int> d; // 声明 TDerived<int, int> 类对象 d
        d.print(); // 输出 data1 和 data2 的值
        TDerived<string, string> d2; // 声明 TDerived<string, string> 类对象 d2
        d2.print(); // 输出 data1 和 data2 的值
        return 0;
    }

【执行结果】

  • 该程序中定义了一个类模板 TBase 和一个继承自 TBase 的类模板 TDerived,其中 TBase 类包含一个私有数据成员 data1 和一个公有成员函数 print,而 TDerived 类包含一个私有数据成员 data2,以及一个公有成员函数 print。在 print 函数中,使用 TBase<T1>::print() 调用了基类 TBase 中的 print 函数,并将类中的数据成员值输出到标准输出流中。
  • 在 main 函数中,首先定义了一个名为 d 的 TDerived<int, int> 类对象,并调用它的 print 函数输出数据成员的值。接着定义了一个名为 d2 的 TDerived<string, string> 类对象,并调用它的 print 函数输出数据成员的值。
  • 在该程序中,因为基类 TBase 和派生类 TDerived 分别定义了不同的模板参数类型,所以在实例化 TDerived 类对象时会使用默认构造函数(即未提供值时数据成员被默认初始化为 0 或空字符串(“”).)。所以,输出结果为 “TBase::0\nTDerived::0\nTBase::\nTDerived::\n”。
TBase::0
TDerived::0
TBase::
TDerived::

【示例四】 类模板的定义和使用

【示例代码】该程序共定义了两个类模板 TBase 和 TDerived

  • TBase 模板定义了一个公有数据成员 data1 和一个成员函数 printprint 函数负责输出数据成员 data1 的值
  • TDerived 模板继承自 TBase 模板并定义了一个公有数据成员 data2 和一个成员函数 printprint 函数中使用 TBase<T1>::print()
#include <iostream>
#include <string>

using namespace std;

template <class T>
class TBase {
public:
    T data1; // 公有数据成员 data1
public:
    void print() {
        cout << "TBase::" << data1 << endl; // 成员函数 print 输出 data1 的值
    }
};

template <class T1, class T2>
class TDerived : public TBase<T1> {
public:
    T2 data2; // 公有数据成员 data2
public:
    void print() {
        TBase<T1>::print(); // 成员函数 print 调用基类 TBase 的 print 函数输出 data1 的值
        cout << "TDerived::" << data2 << endl; // 输出 data2 的值
    }
};

int main() {
    TDerived<int, int> d; // 声明 TDerived<int, int> 类对象 d
    d.data1 = 5;    // 给 d 的 data1 赋值 5
    d.data2 = 8;    // 给 d 的 data2 赋值 8
    d.print();  // 输出 data1 和 data2 的值

    TDerived<string, string> d2;    // 声明 TDerived<string, string> 类对象 d2
    d2.data1 = "happy"; // 给 d2 的 data1 赋值 "happy"
    d2.data2 = "new year"; // 给 d2 的 data2 赋值 "new year"
    d2.print(); // 输出 data1 和 data2 的值

    TDerived<int, string> d1;    // 声明 TDerived<int, string> 类对象 d1
    d1.data1 = 2020;    // 给 d1 的 data1 赋值 2020
    d1.data2 = "good luck"; // 给 d1 的 data2 赋值 "good luck"
    d1.print(); // 输出 data1 和 data2 的值

    return 0;
}

【代码详解】

  • 这段代码定义了一个模板类 TBase 和一个从 TBase 类继承而来的模板类 TDerived,并在 main 函数中声明和使用了这两个类的实例。
  • 头文件声明和命名空间:
    #include <iostream>
    #include <string>
    
    using namespace std;
  • 这部分代码定义了一个模板类 TBase,其中成员变量 data1 是公有的,成员函数 print 用来输出 data1 的值。TBase 类型的对象 data1 是模板类型 T 的一个对象:
    template <class T>
    class TBase {
    public:
        T data1; // 公有数据成员 data1
    public:
        void print() {
            cout << "TBase::" << data1 << endl; // 成员函数 print 输出 data1 的值
        }
    };
  • 这部分代码定义了一个继承自 TBase 模板类的模板类 TDerived,其中 T1 和 T2 是两个不同的模板参数。TDerived 类型的对象 data2 和 data1 分别是 T2 和 T1 类型的对象。成员函数 print 输出 data1 的值,它首先调用基类 TBase 的 print 函数输出 data1 的值,然后输出 data2 的值:
    template <class T1, class T2>
    class TDerived : public TBase<T1> {
    public:
        T2 data2; // 公有数据成员 data2
    public:
        void print() {
            TBase<T1>::print(); // 成员函数 print 调用基类 TBase 的 print 函数输出 data1 的值
            cout << "TDerived::" << data2 << endl; // 输出 data2 的值
        }
    };
  • 这部分代码定义了 main 函数。在函数中,分别声明了三个模板类 TDerived 的对象 dd2 和 d1,分别使用了不同的模板参数类型。通过分别为对象的成员变量 data1 和 data2 分别赋值后,调用 print 函数输出不同类型的模板参数对象的成员变量值。每一个 TDerived 对象都分别调用了一次 print 函数:
    int main() {
        TDerived<int, int> d; // 声明 TDerived<int, int> 类对象 d
        d.data1 = 5;    // 给 d 的 data1 赋值 5
        d.data2 = 8;    // 给 d 的 data2 赋值 8
        d.print();  // 输出 data1 和 data2 的值
    
        TDerived<string, string> d2;    // 声明 TDerived<string, string> 类对象 d2
        d2.data1 = "happy"; // 给 d2 的 data1 赋值 "happy"
        d2.data2 = "new year"; // 给 d2 的 data2 赋值 "new year"
        d2.print(); // 输出 data1 和 data2 的值
    
        TDerived<int, string> d1;    // 声明 TDerived<int, string> 类对象 d1
        d1.data1 = 2020;    // 给 d1 的 data1 赋值 2020
        d1.data2 = "good luck"; // 给 d1 的 data2 赋值 "good luck"
        d1.print(); // 输出 data1 和 data2 的值
    
        return 0;
    }

【执行结果】

  • 该结果是由于程序定义了模板类 TBase 和 TDerivedTDerived 是 TBase 的一个子类,每个类都有一个名为 data1 和 data2 的变量,分别为 T1 和 T2 类型。 main 函数创建了TDerived 类的三个对象并为它们的数据成员赋值。然后通过调用每个对象的 print() 函数,输出了每个对象的 data1 和 data2
TBase::5
TDerived::8
TBase::happy
TDerived::new year
TBase::2020
TDerived::good luck

【示例五】 类模板的定义

【示例代码】该程序定义了两个类模板,分别是 TBase 和 TDerived:

  • TBase 类模板中定义了私有成员变量 data1 和成员函数 print(),它们的访问权限是 private 和 public
  • TDerived 类模板继承于 TBase<int> 模板,并定义了私有成员变量 data2 和公有成员函数 print()
#include <iostream>
#include <string>
using namespace std;

template<class T>
class TBase // 类模板
{
    T data1;
public:
    void print()
    {
        cout << "TBase::" << data1 << endl;
    }
};

template<class T2>
class TDerived: public TBase<int> // 类模板继承于模板类
{
    T2 data2;
public:
    void print()
    {
        TBase<int>::print(); // 调用基类 TBase 的 print 函数输出 data1 的值
        cout << "TDerived::" << data2 << endl;
    }
};

int main()
{
    TDerived<int> d; 
    d.print();
    TDerived<string> d2;
    d2.print();

    return 0;
}

【代码详解】

  • 该程序定义了两个类模板,分别是 TBase 和 TDerivedTBase 类模板中定义了私有成员变量 data1 和成员函数 print(),它们的访问权限是 private 和 publicTDerived 类模板继承于 TBase<int> 模板,并定义了私有成员变量 data2 和公有成员函数 print()

  • 在 main 函数中,程序分别声明了两个模板类 TDerived<int> 和 TDerived<string> 的对象 d 和 d2,并调用它们的 print() 函数。输出结果中,前面的字符串 "TBase::" 和 "TDerived::" 是在 print() 函数中输出的,后面的数值或字符串是对象的成员变量值。

  • 头文件声明和命名空间:
    #include <iostream>
    using namespace std;
  • 这部分代码定义了一个 TBase 类模板,该模板有一个私有成员变量 data1,还定义了一个公共成员函数 print(),用于输出 data1 的值:
    template<class T>
    class TBase // 类模板
    {
        T data1;
    public:
        void print()
        {
            cout << "TBase::" << data1 << endl;
        }
    };
  • 这部分代码定义了一个 TDerived 类模板,该模板继承了 TBase<int> 模板,并有一个类型为 T2 的公共成员变量 data2。成员函数 print() 输出了 data1 的值和 data2 的值,其中通过在基类名字和函数名之间使用双冒号 :: 的方法调用了基类 TBase 的 print() 方法:
    template<class T2>
    class TDerived: public TBase<int> // 类模板继承于模板类
    {
        T2 data2;
    public:
        void print()
        {
            TBase<int>::print(); // 调用基类 TBase 的 print 函数输出 data1 的值
            cout << "TDerived::" << data2 << endl;
        }
    };
  • 这部分代码在 main() 函数中使用 TDerived 类模板实例化了两个对象 d 和 d2,分别是 TDerived<int> 和 TDerived<string>。接着,该程序调用每个对象的 print() 方法,分别输出 data1 和 data2 的值:
    int main()
    {
        TDerived<int> d; 
        d.print();
        TDerived<string> d2;
        d2.print();
    
        return 0;
    }

【执行结果】

  • 该程序使用了类模板 TBase 和 TDerived,并在 main 函数中创建了 TDerived<int> 和 TDerived<string> 的对象 d 和 d2。因为 TDerived 类继承自 TBase<int>,所以在 TDerived<int> 中,T 被指定为 int,在 TDerived<string> 中,T 被指定为默认的 string

  • 程序首先调用 d 对象的 print() 函数,该函数调用 TBase<int> 的成员函数 print(),输出 “TBase::0”,再输出 data2 的值 “”,这是因为 TDerived<int> 类模板中的 data2 是未初始化的,所以其值为默认值。接下来,程序再次调用 d2 对象的 print() 函数,同样调用 TBase<int> 的 print() 函数,输出 “TBase::”,再输出 data2 的值 “”。

  • 因此,程序的输出结果为:

TBase::0
TDerived::
TBase::
TDerived::

【示例六】类模板和继承的使用方法和共同使用方式

【示例代码】通过两个模板类建立了类之间的继承关系,通过继承可以方便地实现代码的复用,同时使用类模板又使得代码可以适用于不同数据类型的情况:

#include <iostream>
#include <string>
using namespace std;

template<class T>
class TBase // 定义类模板 TBase
{
public:
    T data1; // 一个公有成员变量
    void print() // 一个公有成员函数,输出 data1 的值
    {
        cout << "TBase::" << data1 << endl;
    }
};

template<class T2>
class TDerived: public TBase<int> // 定义类模板 TDerived,它继承于模板类 TBase<int>
{
public:
    T2 data2; // 一个公有成员变量
    void print() // 一个公有成员函数,输出 data1 的值及 data2 的值
    {
        TBase<int>::print(); // 调用基类 TBase 的 print 函数输出 data1 的值
        cout << "TDerived::" << data2 << endl;
    }
};

int main()
{
    TDerived<int> d; // 声明一个 TDerived<int> 对象 d
    d.data1 = 5; // 初始化对象 d 的 data1 成员
    d.data2 = 8; // 初始化对象 d 的 data2 成员
    d.print(); // 调用对象 d 的 print 成员函数,输出结果

    TDerived<string> d2; // 声明一个 TDerived<string> 对象 d2
    d2.data1 = 2020; // 初始化对象 d2 的 data1 成员
    d2.data2 = "good luck"; // 初始化对象 d2 的 data2 成员
    d2.print(); // 调用对象 d2 的 print 成员函数,输出结果

    return 0; // 返回 0,表示程序运行正常结束
}

【代码详解】

  • 该程序使用类模板 TBase 和 TDerived,并在 main 函数中创建了 TDerived<int> 和 TDerived<string> 的对象 d 和 d2。其中,TBase 类模板是一个简单类,包含一个 data1 成员变量和一个 print() 成员函数;TDerived 类模板是一个继承于 TBase<int> 模板的类模板,增加了一个 data2 成员变量和 print() 成员函数。

  • 在 main() 函数中,程序首先声明了一个 TDerived<int> 的对象 d,对其 data1 和 data2 成员进行了初始化,并调用它的 print() 函数输出结果。然后声明了一个 TDerived<string> 的对象 d2,对其 data1 和 data2 成员进行了初始化,并调用它的 print() 函数输出结果。

  •  头文件声明和命名空间:
    #include <iostream>
    #include <string>
    using namespace std;
  • 定义了一个类模板 TBase,该类模板包含一个公有成员 data1 和一个公有成员函数print(),用于输出 data1 的值:
    template<class T>
    class TBase // 定义类模板 TBase
    {
    public:
        T data1; // 一个公有成员变量
        void print() // 一个公有成员函数,输出 data1 的值
        {
            cout << "TBase::" << data1 << endl;
        }
    };
  • 定义了类模板 TDerived,它是从模板类 TBase<int> 继承而来。定义了一个公有成员data2 和一个公有成员函数 print(),用于输出 data1 和 data2 的值。在 print() 函数中,为了调用基类 TBase 的 print() 函数输出 data1 的值,使用了 TBase<int>::print() 语法:
    template<class T2>
    class TDerived: public TBase<int> // 定义类模板 TDerived,它继承于模板类 TBase<int>
    {
    public:
        T2 data2; // 一个公有成员变量
        void print() // 一个公有成员函数,输出 data1 的值及 data2 的值
        {
            TBase<int>::print(); // 调用基类 TBase 的 print 函数输出 data1 的值
            cout << "TDerived::" << data2 << endl;
        }
    };
  • 在main函数中,先声明了一个TDerived<int>对象d,然后分别对d的data1和data2成员进行了初始化,最后调用d的print()函数。接着又声明了一个TDerived<string>对象d2,然后也分别对d2的data1和data2成员进行了初始化,最后调用d2的print()函数。最后返回0,表示程序执行成功结束:
    int main()
    {
        TDerived<int> d; // 声明一个TDerived<int>对象d
        d.data1 = 5; // 初始化对象d的data1成员
        d.data2 = 8; // 初始化对象d的data2成员
        d.print(); // 调用对象d的print成员函数,输出结果
    
        TDerived<string> d2; // 声明一个TDerived<string>对象d2
        d2.data1 = 2020; // 初始化对象d2的data1成员
        d2.data2 = "good luck"; // 初始化对象d2的data2成员
        d2.print(); // 调用对象d2的print成员函数,输出结果
    
        return 0; // 返回0,表示程序运行正常结束
    }

【执行结果】

  • 对象 d 的 data1 成员初始化为 5 ,data2 成员初始化为 8 。调用对象 d 的 print() 函数,首先输出 "TBass::5",然后输出 "TDerived::8"。

  • 对象 d2 的 data1 成员初始化为 2020,data2 成员初始化为 "good luck"。调用对象 d2 的 print() 函数,首先输出 "TBase::2020",然后输出 "TDerived::good luck"。

TBase::5
TDerived::8
TBase::2020
TDerived::good luck

猜你喜欢

转载自blog.csdn.net/qq_39720249/article/details/131403357