泛型编程(简单介绍,通俗易懂)
泛型编程:一份代码,适用多种数据类型,不需要重写很多份。
泛型编程大多c++程序员了解最少地部分,《Effective C++》条款48有讲到。
例子:
假设你要写一个函数,找到两个数字中较大的那个。
int getMaxInt(int a, int b) {return a > b ? a : b;
}double getMaxDouble(double a, double b) {return a > b ? a : b;
}char getMaxChar(char a, char b) {return a > b ? a : b;
}
这样有几个问题:
代码重复,维护成本高
如果增加新的类型(比如
float
、long
、std::string
),还得再写一遍
可以用泛型编程来解决这个问题:
用模板(template)实现泛型:
template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}int main() {cout << getMax(3, 7) << endl; // intcout << getMax(3.5, 7.2) << endl; // doublecout << getMax('a', 'z') << endl; // charcout << getMax(string("abc"), string("xyz")) << endl; // stringreturn 0;
}
输出:
7
7.2
z
xyz
1. template <typename T>
template
关键字表示:这是一个模板。<typename T>
表示:这里定义了一个占位类型,叫做T
。T
不是具体的类型,比如int
、double
、string
,而是一个变量,在调用函数的时候由编译器自动替换。
可以把 T
理解成一个“万能胶”,用的时候会自动变成你需要的类型。
2. T getMax(T a, T b)
这行是函数声明,
T
是返回值类型。getMax
是函数名。(T a, T b)
表示参数类型是T
,也就是说:参数和返回值都是同一个类型。
所以,如果你传入 int
,那么 T
会被替换成 int
;
如果你传入 double
,那么 T
会被替换成 double
。
3. 编译器是怎么处理的
当你写:
cout << getMax(3, 7) << endl;
编译器会在编译期做“模板实例化”,相当于自动生成:
int getMax(int a, int b) {return a > b ? a : b;
}
如果你写:
cout << getMax(3.5, 7.2) << endl;
编译器又会自动生成:
double getMax(double a, double b) {return a > b ? a : b;
}
所以我们只写了一次代码,但编译器帮我们生成了多个版本的函数。
泛型编程的好处
特点 | 没有泛型 | 使用泛型 |
---|---|---|
代码复用 | 需要为不同类型写很多函数 | 一份代码支持多种类型 |
可维护性 | 修改逻辑要改多份 | 改一处即可 |
类型安全 | 可能需要用 void* 或强制转换 | 编译器自动检查类型 |
性能 | 可能需要运行时判断 | 模板在编译期生成专用代码,零开销 |
泛型编程的应用场景
泛型编程在 C++ 中应用非常广泛,比如:
STL(标准模板库)
std::vector<int>
、std::vector<string>
std::map<string, int>
std::sort()
、std::find()
智能指针
std::unique_ptr<T>
、std::shared_ptr<T>
自定义容器和算法
例如 std::vector
就是泛型容器:
std::vector<int> v1; // int 类型的动态数组
std::vector<double> v2; // double 类型的动态数组
std::vector<string> v3; // string 类型的动态数组
vector
只写了一份模板代码,C++ 编译器会在编译期自动生成对应类型的代码,这叫做 模板实例化。
question:既然 std::vector<int>
写了 int
,那不是我手动指定了类型吗?这不算泛型自动识别吧?
tips:泛型不是自动识别,而是自动生成
泛型编程的核心思想是:
写一份模板代码,让编译器根据你指定的类型,自动生成对应的实现。
std::vector<int> v1;
里面的 int
确实是你写的,但关键点在于:
我们只写了一份
vector
模板你告诉编译器“我要一个
int
版本的vector
”编译器会在编译期生成一个专门存
int
的类
所以,虽然你要告诉编译器类型,但你不需要为每种类型手动写类,这就是泛型的强大之处。