// 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;
}