#C++ Day33 Basic Data Structure Chapter2 and C++ Generic Programming Day2 November 18 2025

// C++ Generic Programming – Chapter 2 – Function Templates’ differences in Calling Rules between Regular Functions-2.4.1-Prioritize Calling General Functions

// C++ 泛型编程 -第二章-函数模版与普通函数调用规则区别 -2.4.1-优先调用普通函数

#include <iostream>

using namespace std;

// 普通函数和函数模版的调用规则区别

// 普通函数和函数模版可以重名

// 当普通函数和函数模版 函数名相同 数量也相同的时候 就会优先调用普通函数

int add(int a, int b)

{

    cout << “调用普通函数” << endl;

    return a + b;

}

// int add(int a, int b);

// 就算这个普通函数是声明 编译器也会优先调这里 而不是函数模版

// 显示无法解析的外部符号 普通函数没有实现

template <typename T>

T add(T a, T b)

{

    cout << “调用函数模版” << endl;

    return a + b;

} // 跟模版在不在没关系

int main()

{

    int a = 1, b = 2;

    add(a, b);

    // 编译器规则:如果普通函数和函数模版都可以调用的情况下 会优先调用普通函数

    return 0;

}

// C++ Generic Programming – Chapter 2 – Function Templates’ differences in Calling Rules between Regular Functions-2.4.2 Forced Calling Function Templates

//  C++ 泛型编程 -第二章-函数模版与普通函数调用规则区别 -2.4.2-优先调用函数模版

#include <iostream>

using namespace std;

// 普通函数和函数模版的调用规则区别

// 普通函数和函数模版可以重名

// 当普通函数和函数模版 函数名相同 数量也相同的时候 就会优先调用普通函数

int add(int a, int b)

{

    cout << “调用普通函数” << endl;

    return a + b;

}

// int add(int a, int b);

// 就算这个普通函数是声明 编译器也会优先调这里 而不是函数模版

// 显示无法解析的外部符号 普通函数没有实现

template <typename T>

T add(T a, T b)

{

    cout << “调用函数模版” << endl;

    return a + b;

} // 跟模版在不在没关系

int main()

{

    int a = 1, b = 2;

    // add(a, b);

    // 怎么强制调用函数模版 只要在这里显式指定类型就可以了

    // add<int>(a, b); // 普通函数没有这个玩意  加上<int>就是显式指定类型

    add<>(a, b); // 也可以不写int 变成空参数列表

    // 编译器会先指定一个函数模版 再进行自动类型推导

    // 结论:当普通函数和函数模版都可以调用时 可以加上参数列表让编译器强制调用函数模版

    // 编译器规则:如果普通函数和函数模版都可以调用的情况下 会优先调用普通函数

    return 0;

}

// C++ Generic Programming – Chapter 2 – Function Templates’ differences in Calling Rules between Regular Functions-2.4.3 Priority Matching of Function Templates

//  C++ 泛型编程 -第二章-函数模版与普通函数调用规则区别 -2.4.3 – 函数模版的优先匹配

#include <iostream>

using namespace std;

// 即使当不显式指定类型的时候 也能让它调用函数模版

int add(int a, int b)

{

    cout << “调用普通函数” << endl;

    return a + b;

}

template <typename T>

T add(T a, T b)

{

    cout << “调用函数模版” << endl;

    return a + b;

}

int main()

{

    double a = 1, b = 2;

    add(a, b);

    // 普通函数支持隐式类型转换 double转成int 走隐式类型转换

    // 如果是第二个函数 走自动类型推导 可以推导成double

    // 这里的规则:这里是调用函数模版,原因是首先编译器会尝试最匹配的普通函数的版本

    // 找到普通函数的版本 能够完美匹配参数类型 就会调用普通函数

    // 因为这里发生了隐式转换 所以这里不叫完美匹配了

    // 要没有任何转换的情况下才叫完美匹配

    // 所以实际上没有找到合适的版本 这时候编译器就会考虑进行实例化 生成一个合适的函数版本进行调用 所以才会调用这个函数模版

    return 0;

}

// C++ Generic Programming – Chapter 2 – 2.5 Function Templates with Multiple Parameters

//  C++ 泛型编程 -第二章 – 2.5 – 多参数函数模版

#include <iostream>

using namespace std;

/*

template <typename T1, typename T2, typename T3> // 这里三个类型 当然可以继续往后加

T1 add(T2 a, T3 b)

{

    T1 c = a + b;

    return c;

}

*/

template <typename T2, typename T3> // 这里三个类型 当然可以继续往后加

T2 add(T2 a, T3 b)

{

    // 在正确匹配之后 我想看看T2和T3到底是什么类型 可以通过typeid(T2).name()关键字打印出来

    cout << typeid(T2).name() << endl;

    cout << typeid(T3).name() << endl;

    T2 c = a + b;

    return c;

}

int main()

{

    // double r = add(4.0, 8);

    // 首先T1我们认为它是一个double类型 然后调用add函数 并且传一个double类型和一个整型进去

    // 这样编译通不过 提示没有与参数类型匹配的模版

    // 根据4.0 我们可以推导T2它是个double,根据8 我们可以推导T3它是个int

    // 但是这个T1的类型是推不出来的

    // 结论一:返回值类型(double)无法作为(T1)推导依据

    // 结论二:一旦有类型参数不能推导 就会编译失败

    // 解决:把T1去掉 然后把T1改为T2 这样就完美解决问题了

    double r = add(4.0f, 88881281881); // float类型被推导成float T3类型被推导成了int64 这个int64其实就是longlong 代表 64位整型

    /*

    1、函数模版支持多个类型参数

    2、一旦有类型不能推导,就会导致编译失败

    3、返回值类型无法作为推导依据

    */

    return 0;

}

// C++ Generic Programming – Chapter 3 – Class Templates – 3.1 Dynamic Array Class

//  C++ 泛型编程 -第三章 -类模版- 3.1 – 动态数组类

#include <iostream>

using namespace std;

// 在C++中 你如果定义一个原生的静态数组 数组大小必须是常量 不能是变量

class DynamicArray

{

private:

    int* elements; // 这个成员就是p

    int size;      // 代表数组的一个大小

public:

    DynamicArray(int n) : size(n)

    { // 构造函数 名字一样 传参是一个整数n 初始化列表 把这个n赋值给这个size

        // 在构造函数里面 把这个elements动态内存给它申请出来

        elements = new int[n];

        // size和n都可以 ,因为n和size的大小是一模一样的

        // 那么在这个构造函数里面 就相当于申请了长度为size的动态数组的内存

    }

    ~DynamicArray()

    {

        // 申请了还需要进行销毁 不然容易导致内存泄漏

        // 我们把销毁放在析构函数里面

        delete[] elements;

        // 因为销毁的是一块数组内存 所以调用delete[]

    }

    // 由于我们模拟的是一个数组 所以对于这个类 我们要实现一个中括号的操作 运算符重载

    // 其实就是实现一个函数 这个函数它有个返回值 返回值是个整数 那就返回一个整型

    int& operator[](int index)  // 如果不写引用 我得到的只是一个值 我不能对它进行赋值 但是我得返回这个元素本身 而不能只是一个值

        //引用去掉后 我用没关系 但是无法对它进行赋值了

    {                           // 加上一个引用 传参是一个索引

        return elements[index]; // 返回一个elements[index]的元素就可以了

    }

};

int main()

{

    int n = 10;

    // int a[n];//报错

    // 在C++中 你如果定义一个原生的静态数组 数组大小必须是常量 不能是变量 标准C++过不了

    // 那么如何在标准C++中实现 给定一个变量n 生成一个长度为n的数组

    // 我们只需要定义一个指针 然后用new运算符实现就可以了

    int* p = new int[n]; // 相当于在堆上申请了 n乘上sizeof(int)的字节 赋值给了指针p

    // 这时候这个p实际上就是动态数组的首地址了 我就可以通过对p进行下标操作或者指针偏移,访问到这块连续内存中的数据了

    // 这个就是动态数组

    // 而动态数组类就是把它进行一层封装 把p变成类的成员变量

    // 动态数组类实现出来了

    DynamicArray da(100);

    da[1] = 3;

    da[9] = 4;

    cout << da[0] << ‘ ‘ << da[1] << endl;//因为数组没有初始化 所以da[0]是一个乱的值

    //如果要实现char double 或者其他类型 的动态数组 而不多写冗余的代码,我该怎么办: 使用类模板

    return 0;

}