什么是泛型编程

什么是泛型编程

泛型编程就是传递变量类型的编程
主要作用1:节省代码
主要作用2:传递参数类型
主要作用3:写底层架构

一 节省代码

// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<typename T>  // 或 template<class T>
void ShowT(T a) {

    cout << a << endl;
   
}

void Show(int a)
{
    cout << a << endl;
}
void Show(double a)
{
    cout << a << endl;
}

void Show(char a)
{
    cout << a << endl;
}


int main()
{
    int a = 99;
    double b = 9.6666;
    char c = 'a';
    //Show(a);
    //Show(b);
    //Show(c);

    ShowT(a);
    ShowT(b);
    ShowT(c);

}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件

传递参数类型(泛型编程又称传递参数编程,可见之重要)

// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<typename T>  // 或 template<class T>
void ShowT(T a) {

    cout << a << endl;
   
}

void Show(int a)
{
    cout << a << endl;
}
void Show(double a)
{
    cout << a << endl;
}

void Show(char a)
{
    cout << a << endl;
}


int main()
{
    int a = 99;
    double b = 9.6666;
    char c = 'a';
    //Show(a);
    //Show(b);
    //Show(c);

    ShowT(a);
    ShowT(b);
    ShowT(c);
    ShowT<int>(a);
    ShowT<double>(b);
    ShowT<char>(c);

}

// ConsoleApplication9.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

template<typename T>  // 或 template<class T>
void ShowT(T a) {

    cout << a << endl;
   
}

void Show(int a)
{
    cout << a << endl;
}
void Show(double a)
{
    cout << a << endl;
}

void Show(char a)
{
    cout << a << endl;
}


template<typename T1, typename T2>  // 或 template<class T>
void MyTest(T1 a, T2 b)
{

    std::cout << "Type of T1: " << typeid(T1).name() << std::endl;
    std::cout << "Type of T2: " << typeid(T2).name() << std::endl;


}


int main()
{
    int a = 99;
    double b = 9.6666;
    char c = 'a';
    //Show(a);
    //Show(b);
    //Show(c);

    //ShowT(a);
    //ShowT(b);
    //ShowT(c);
    //ShowT<int>(a);
    //ShowT<double>(b);
    //ShowT<char>(c);

    MyTest<int,double>(1,2.1);

}

在 C++ 中,获取类的类型(即类型信息)可以通过多种方式实现,具体取决于使用场景。以下是常见的方法:


1. 使用 typeid 运算符(RTTI)

typeid 是 C++ 运行时类型识别(RTTI)机制的一部分,返回一个 std::type_info 对象,可用于获取类型名称或比较类型。

#include <iostream>
#include <typeinfo>

class MyClass {};

int main() {
    MyClass obj;
    const std::type_info& ti = typeid(obj);
    
    std::cout << "Type name: " << ti.name() << std::endl; // 输出类型名称(可能是编译器修饰的)
    std::cout << "Is MyClass? " << (ti == typeid(MyClass)) << std::endl; // 比较类型
    
    return 0;
}

注意事项:

  • ti.name() 返回的名称可能是编译器修饰的(如 4MyClass),不同编译器表现不同。
  • 需要启用 RTTI(默认开启,可通过编译器选项 -fno-rtti 禁用)。
  • 适用于多态类型(有虚函数的类)时能获取动态类型,否则获取静态类型。

2. 使用 decltype(编译时类型推导)

decltype 用于推导表达式的类型,常用于模板元编程或返回类型推导。

#include <iostream>

class MyClass {};

int main() {
    MyClass obj;
    decltype(obj) obj2; // obj2 的类型是 MyClass
    
    std::cout << "Type of obj2 is MyClass" << std::endl;
    return 0;
}

典型用途:

  • 在模板中推导参数类型:
    template<typename T>
    auto get_value(T t) -> decltype(t.get_value()) {
        return t.get_value();
    }
    
  • C++14 起用于泛型 lambda:
    auto lambda = [](auto x) { return decltype(x)(0); };
    

3. 使用 auto 和类型别名

通过 auto 自动推导变量类型,或使用 using/typedef 定义类型别名。

#include <iostream>

class MyClass {};

int main() {
    MyClass obj;
    auto obj2 = obj; // obj2 的类型自动推导为 MyClass
    
    using MyType = MyClass; // 类型别名
    MyType obj3;
    
    std::cout << "obj2 is of type MyClass" << std::endl;
    return 0;
}

4. 模板参数类型提取

在模板中,可以通过 typenameclass 获取模板参数的类型。

#include <iostream>

template<typename T>
class Wrapper {
public:
    using value_type = T; // 定义内部类型别名
};

int main() {
    Wrapper<int> w;
    Wrapper<int>::value_type x = 42; // x 的类型是 int
    
    std::cout << "x = " << x << std::endl;
    return 0;
}

5. C++17 的 std::void_t 和 SFINAE 类型检测

通过模板技巧在编译时检测类的成员类型或属性。

#include <iostream>
#include <type_traits>

class MyClass {
public:
    using value_type = int;
};

// 检测类是否有 value_type 成员
template<typename T, typename = void>
struct has_value_type : std::false_type {};

template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};

int main() {
    std::cout << "MyClass has value_type? " 
              << has_value_type<MyClass>::value << std::endl; // 输出 1 (true)
    return 0;
}

6. 获取类的 this 类型(C++11 起)

使用 decltypestd::declval 获取类的 this 类型。

#include <iostream>
#include <utility>

class MyClass {
public:
    auto get_this_type() -> decltype(*this) { 
        return *this; 
    }
    
    // C++14 起可简化为:
    // auto get_this_type() { return *this; }
};

int main() {
    MyClass obj;
    auto obj2 = obj.get_this_type(); // obj2 的类型是 MyClass
    return 0;
}

7. C++20 的 Concepts(类型约束)

通过 Concepts 显式约束模板参数的类型。

#include <iostream>
#include <concepts>

template<typename T>
concept IsMyClass = requires(T t) {
    { t } -> std::convertible_to<T>; // 简单约束
};

class MyClass {};

void print_type(IsMyClass auto obj) {
    std::cout << "obj is of type MyClass or compatible" << std::endl;
}

int main() {
    MyClass obj;
    print_type(obj); // 匹配成功
    return 0;
}

总结

方法适用场景特点
typeid运行时类型识别需要 RTTI,名称可能被修饰
decltype编译时类型推导适用于表达式和变量
auto自动类型推导简化代码,但隐藏具体类型
模板参数泛型编程通过 typename 提取类型
SFINAE/void_t编译时类型检测复杂但强大
C++20 Concepts类型约束语义清晰,替代部分 SFINAE

根据需求选择合适的方法:

  • 需要运行时信息 → typeid
  • 需要编译时类型推导 → decltype 或模板参数。
  • 需要类型约束 → C++20 Concepts。
  • 需要检测类型特征 → SFINAE 或 void_t

C++泛型编程教程:从模板到STL的深度实践

一、泛型编程的核心思想

泛型编程(Generic Programming)通过参数化类型实现代码复用,其核心在于编写与具体数据类型无关的通用算法。例如,一个排序算法无需为intdouble或自定义类型重复实现,只需通过模板机制生成不同版本的代码。这种范式在C++中通过**模板(Templates)**实现,其优势体现在:

  • 代码复用性:STL中的vector<T>可存储任意类型,避免为每种类型重复实现动态数组。
  • 类型安全:编译期类型检查消除运行时类型错误(如Java泛型的类型擦除问题)。
  • 性能优化:生成的代码与原生类型操作效率相当,如vector<int>的访问速度接近数组。

二、函数模板:通用算法的基石

1. 基本语法与实例化

template<typename T>  // 或 template<class T>
T max(T a, T b) {
    return a > b ? a : b;
}

int main() {
    cout << max(3, 5);          // 隐式实例化:T=int
    cout << max<double>(3.14, 2.71); // 显式实例化:T=double
}
  • 隐式推导:编译器根据参数类型自动确定T
  • 显式指定:通过<类型>明确指定模板参数,解决多参数类型不一致问题。

2. 模板特化:定制化实现

为特定类型提供优化逻辑:

// 全特化:针对char*类型
template<>
const char* max<const char*>(const char* a, const char* b) {
    return strcmp(a, b) > 0 ? a : b;
}

// 偏特化(C++不支持直接偏特化函数模板,需通过类模板间接实现)

3. 非类型模板参数

传递编译期常量:

template<typename T, int N>
class FixedArray {
    T data[N];
public:
    T& operator[](int i) { return data[i]; }
};

FixedArray<int, 10> arr; // 创建固定大小为10的整型数组

三、类模板:通用数据结构的实现

1. 基本类模板

template<typename T>
class Stack {
    T* data;
    int top;
public:
    Stack(int size) : data(new T[size]), top(0) {}
    void push(T val) { data[top++] = val; }
    T pop() { return data[--top]; }
};

Stack<string> strStack; // 存储字符串的栈

2. 模板参数默认值

template<typename T = int, int N = 100>
class Buffer {
    T data[N];
};

Buffer<> buf1; // T=int, N=100
Buffer<double, 200> buf2; // T=double, N=200

3. 类模板的特化

为指针类型优化内存管理:

template<typename T>
class SmartPtr {
    T* ptr;
public:
    SmartPtr(T* p) : ptr(p) {}
    ~SmartPtr() { delete ptr; }
};

// 特化版本:处理字符指针(不调用delete)
template<>
class SmartPtr<char*> {
    char* ptr;
public:
    SmartPtr(char* p) : ptr(p) {}
    ~SmartPtr() { /* 不释放内存 */ }
};

四、可变参数模板:C++11的扩展

1. 参数包展开

// 递归展开
void print() {} // 终止函数

template<typename T, typename... Args>
void print(T first, Args... args) {
    cout << first << " ";
    print(args...); // 递归调用
}

print(1, 3.14, "hello"); // 输出:1 3.14 hello

2. 折叠表达式(C++17)

简化参数包运算:

template<typename... Args>
auto sum(Args... args) {
    return (args + ...); // 展开为 (a1 + a2 + ... + an)
}

cout << sum(1, 2, 3); // 输出6

五、STL:泛型编程的典范

1. 容器(Containers)

  • 顺序容器
    vector<int> vec = {1, 2, 3}; // 动态数组
    deque<double> dq;            // 双端队列
    list<string> lst;            // 双向链表
    
  • 关联容器
    set<int> s;                  // 红黑树实现的有序集合
    map<string, int> m;          // 键值对映射
    unordered_map<int, string> um; // 哈希表实现的无序映射
    

2. 算法(Algorithms)

vector<int> v = {3, 1, 4, 1, 5};
sort(v.begin(), v.end());     // 排序
find(v.begin(), v.end(), 4);  // 查找
for_each(v.begin(), v.end(), [](int x) { cout << x << " "; });

3. 迭代器(Iterators)

vector<int>::iterator it = v.begin(); // 随机访问迭代器
list<string>::iterator lit = lst.begin(); // 双向迭代器

六、高级技巧与注意事项

1. 显式实例化与分离编译

  • 显式实例化
    template class Stack<float>; // 强制生成Stack<float>的代码
    
  • 分离编译:将模板声明放在.h文件,定义放在.tpp.h中(需包含实现)。

2. 模板元编程(TMP)

编译期计算示例:

template<int N>
struct Factorial {
    static const int value = N * Factorial<N-1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

cout << Factorial<5>::value; // 输出120

3. 性能权衡

  • 代码膨胀:每个模板实例化生成独立代码,需权衡复用性与二进制体积。
  • 编译时间:复杂模板(如元编程)显著增加编译时间,可通过extern template减少重复实例化。

七、实战案例:通用矩阵运算库

template<typename T>
class Matrix {
    vector<vector<T>> data;
public:
    Matrix(int rows, int cols) : data(rows, vector<T>(cols)) {}
    
    Matrix operator+(const Matrix& other) {
        Matrix result(data.size(), data[0].size());
        for (int i = 0; i < data.size(); ++i) {
            for (int j = 0; j < data[0].size(); ++j) {
                result.data[i][j] = data[i][j] + other.data[i][j];
            }
        }
        return result;
    }
    
    // 可添加运算符*、转置、行列式计算等通用方法
};

Matrix<double> m1(3, 3), m2(3, 3);
auto m3 = m1 + m2; // 支持double类型矩阵运算

八、总结

C++泛型编程通过模板机制实现了算法与数据类型的解耦,其核心在于:

  1. 模板语法:函数模板、类模板、可变参数模板。
  2. 特化与偏特化:为特定类型提供优化实现。
  3. STL应用:容器、算法、迭代器的协同工作。
  4. 高级特性:模板元编程、编译期计算、性能优化技巧。

掌握泛型编程需结合实践,建议从STL源码分析入手,逐步深入模板元编程等高级主题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值