C++泛型编程2 - 类模板

发布于:2025-06-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

C++ 类模板全面教程

类模板是C++中强大且灵活的特性,它允许我们创建可适用于多种数据类型的类。下面我将从基础到高级,系统性地介绍类模板。

一、类模板基本概念

1.1 什么是类模板

类模板是一种允许我们使用不同类型创建类的蓝图。就像函数模板可以生成针对不同类型参数的函数一样,类模板可以生成针对不同类型参数的类。

1.2 为什么需要类模板

假设你需要一个可以存储任何类型数据的栈,不使用模板就需要为每种类型分别创建类:

class IntStack { /* 实现int栈 */ };
class DoubleStack { /* 实现double栈 */ };
class StringStack { /* 实现string栈 */ };

使用类模板则可以定义一次,适应多种类型:

template <typename T>
class Stack { /* 通用栈实现 */ };

二、类模板基本语法

2.1 基本定义

template <typename T>    // 或者 template <class T>
class MyClass {
    // 类成员定义
};

2.2 示例:一个简单的Array类模板

template <typename T>
class Array {
private:
    T* ptr;
    int size;
  
public:
    Array(int s) : size(s), ptr(new T[s]) {}
    ~Array() { delete[] ptr; }
  
    T& operator[](int index) {
        if (index >= size || index < 0) {
            throw std::out_of_range("Index out of range");
        }
        return ptr[index];
    }
  
    int getSize() const { return size; }
};

2.3 使用类模板

Array<int> intArr(10);      // 创建存储int的数组
Array<double> doubleArr(5); // 创建存储double的数组

三、类模板的定义与使用

3.1 多个类型参数

template <typename T, typename U>
class Pair {
public:
    T first;
    U second;
  
    Pair(const T& f, const U& s) : first(f), second(s) {}
};

Pair<int, double> p1(1, 3.14);
Pair<std::string, bool> p2("success", true);

3.2 默认类型参数

template <typename T, typename U = bool>
class Pair {
public:
    T first;
    U second;
  
    Pair(const T& f, const U& s) : first(f), second(s) {}
};

Pair<int, double> p1(1, 3.14);
Pair<std::string> p2("success", true);	// 使用默认类型

3.3 默认非类型参数

template <typename T = int, int size = 10>
class Container {
    T items[size];
    // ...
};

Container<> defaultContainer;          // 使用默认参数 T=int, size=10
Container<double, 20> customContainer; // 指定参数

3.4 成员函数模板

类内部可以定义成员函数模板:

template <typename T>
class Box {
    T content;
  
public:
    template <typename U>
    void assign(const U& newContent) {
        content = newContent; // 需要U能隐式转换为T
    }
};

3.5 成员函数的类外实现

成员函数类外实现,模板声明必须和类模板一致,但无需给出默认参数。以为类名作用域需要指定类型,即必须用模板类型实例化,才能使用。

template <class T1, class T2>
class 类名{
	T1 函数名(T2 a, T2 b);
}

// 注意类名作用域使用模板参数进行实例化。
template <class T1, class T2>
T1 类名<T1, T2>::函数名(T2 a, T2 b){
	// 函数实现
}

3.6 类模板实例化

与函数模板不同,类模板只能显式实例化,不能隐式实例化。因为函数模板调时可以根据参数自动判断类型,而类模板调用时除了显式指定,没有任何参考依据。

template <class T>
class Complex{
	T real;
	T img;
}

// 使用类模板创建对象
Complex<int> c;

3.7 类模板作为函数参数

1. 普通函数

类模板在普通函数中使用必须实例化为一个具体的类:

template <class T>
class Complex{
	T real;
	T img;
}

Complex<double> add(Complex<double> a, Complex<double> b);
2. 函数模板

方法一:常用,用泛型类型实例化类模板作为函数参数类型。

template <class T>
class Complex{
	T real;
	T img;
}

template <class T>
Complex<T> add(Complex<T> a, Complex<T> b);

方法二:通用所有类,在使用时再指定类。

template <class T>
class Complex{
	T real;
	T img;
}

template <class T>
T add(T a, T b);

四、类模板特化

4.1 全特化

所有类型参数都必须具体化。

template <>
class Array<bool> {
private:
    unsigned char* ptr;
    int size;
  
public:
    // 特化实现,按位存储bool以节省空间
    Array(int s) : size(s), ptr(new unsigned char[(s+7)/8]) {}
    ~Array() { delete[] ptr; }
    // 其他特化实现...
};

4.2 偏特化

只具体化部分类型参数。没有具体化的泛型类型在仍然在<>中。而类定义的实例化中可以使用泛型类型。

template <class T>
class 类名<T, string>{

}

五、类模板中的静态成员

类模板中的static成员对于每个模板实例化都是独立的:

template <typename T>
class Counter {
public:
    static int count;
  
    Counter() { ++count; }
    ~Counter() { --count; }
};

template <typename T>
int Counter<T>::count = 0;

Counter<int> c1, c2;    // Counter<int>::count == 2
Counter<double> c3;     // Counter<double>::count == 1

六、类模板继承

6.1 从类模板继承

template <typename T>
class Base {
    // ...
};

template <typename T>
class Derived : public Base<T> {
    // ...
};

6.2 继承关系中的名称查找

在模板继承中需要使用this->Base<T>::来访问基类成员:

template <typename T>
class Derived : public Base<T> {
public:
    void foo() {
        this->baseMember();  // 使用this->访问基类成员
        Base<T>::baseMember(); // 或使用完整限定名
    }
};

七、类模板与友元

7.1 友元函数

template <typename T>
class MyClass {
    T value;
  
    // 声明友元函数
    friend void printValue(const MyClass<T>& obj) {
        std::cout << obj.value;
    }
};

MyClass<int> obj;
printValue(obj); // 正确调用

7.2 友元类

template <typename T>
class FriendClass; // 前向声明

template <typename T>
class MyClass {
private:
    T secret;
  
    friend class FriendClass<T>; // 声明友元类
};

八、使用技巧与最佳实践

  1. 分离实现与声明:可以将类模板的声明放在头文件,定义放在另一个头文件(如.tpp.ipp),然后在声明文件末尾包含定义文件。

  2. 防止模板膨胀:避免生成过多不必要的模板实例,可通过特化等方式优化。

  3. 明确错误信息:模板错误通常在实例化时才被发现,使用static_assert提供更友好的错误信息:

template <typename T>
class NumericArray {
    static_assert(std::is_arithmetic<T>::value, 
                 "NumericArray requires an arithmetic type");
    // ...
};

总结

类模板特点

  • 只能显式实例化,不能隐式实例化
  • 可以指定默认类型

类模板是C++泛型编程的核心技术之一,通过类模板我们可以:

  1. 编写与类型无关的通用代码
  2. 减少代码重复,提高复用性
  3. 在编译时完成类型检查,确保类型安全
  4. 通过特化提供特定类型的优化实现

掌握类模板能显著提升你的C++编程能力,特别是在开发和维护大型库时。建议从简单例子开始,逐步尝试更复杂的应用场景。


网站公告

今日签到

点亮在社区的每一天
去签到