C++学习:六个月从基础到就业——模板编程:类模板

C++学习:六个月从基础到就业——模板编程:类模板

本文是我C++学习之旅系列的第三十三篇技术文章,也是第二阶段"C++进阶特性"的第十一篇,主要介绍C++中的类模板编程。查看完整系列目录了解更多内容。
在这里插入图片描述


在这里插入图片描述


目录

引言

在上一篇文章中,我们深入探讨了C++函数模板的概念和应用。本文将继续模板编程的旅程,重点讨论类模板,这是C++泛型编程的另一个强大工具。

类模板允许我们创建通用的类,这些类能够处理各种数据类型,而不需要为每种类型编写单独的类定义。C++标准库中的容器(如std::vectorstd::map)就是类模板的典型应用。通过类模板,我们可以编写一次代码,却能创建适用于不同数据类型的类,大大提高了代码的复用性和灵活性。

本文将详细介绍类模板的定义和使用方法、类模板的成员函数实现、模板特化、默认模板参数以及类模板的嵌套与继承等高级特性。我们还将通过实际案例展示类模板在C++编程中的应用。

类模板的基本语法

类模板定义

类模板的定义使用template关键字,后跟尖括号中的模板参数列表,然后是类的定义:

template <typename T>  // 或 template <class T>
class MyContainer {
private:
    T* elements;
    size_t count;
    size_t capacity;

public:
    MyContainer(size_t initialCapacity = 10);
    ~MyContainer();
    
    void push_back(const T& element);
    T& at(size_t index);
    const T& at(size_t index) const;
    size_t size() const { return count; }
};

这里定义了一个简单的容器类模板MyContainer,可以存储任意类型的元素。模板参数T在类定义中用作占位符,表示将来使用类模板时会被具体类型替换。

类模板实例化

要使用类模板,需要指定模板参数的具体类型:

int main() {
    // 创建存储int的容器
    MyContainer<int> intContainer;
    intContainer.push_back(42);
    intContainer.push_back(73);
    
    // 创建存储string的容器
    MyContainer<std::string> stringContainer;
    stringContainer.push_back("Hello");
    stringContainer.push_back("World");
    
    std::cout << "intContainer[0] = " << intContainer.at(0) << std::endl;
    std::cout << "stringContainer[0] = " << stringContainer.at(0) << std::endl;
    
    return 0;
}

当我们声明MyContainer<int>时,编译器会生成一个将T替换为int的类定义。同样,MyContainer<std::string>会生成另一个将T替换为std::string的类定义。这个过程称为类模板实例化

类模板的成员函数

成员函数定义

类模板的成员函数可以在类内定义,也可以在类外定义。在类外定义时,需要使用模板前缀和类限定符:

// 类内定义(如前面例子中的size()函数)
template <typename T>
class MyContainer {
public:
    size_t size() const { return count; }  // 直接在类内定义
    // ...其他声明
};

// 类外定义
template <typename T>
MyContainer<T>::MyContainer(size_t initialCapacity) : 
    elements(new T[initialCapacity]), 
    count(0), 
    capacity(initialCapacity) {
}

template <typename T>
MyContainer<T>::~MyContainer() {
    delete[] elements;
}

template <typename T>
void MyContainer<T>::push_back(const T& element) {
    if (count == capacity) {
        // 扩展容量
        capacity *= 2;
        T* newElements = new T[capacity];
        for (size_t i = 0; i < count; ++i) {
            newElements[i] = elements[i];
        }
        delete[] elements;
        elements = newElements;
    }
    
    elements[count++] = element;
}

template <typename T>
T& MyContainer<T>::at(size_t index) {
    if (index >= count) {
        throw std::out_of_range("Index out of bounds");
    }
    return elements[index];
}

template <typename T>
const T& MyContainer<T>::at(size_t index) const {
    if (index >= count) {
        throw std::out_of_range("Index out of bounds");
    }
    return elements[index];
}

在类外定义成员函数时,每个函数定义前都必须有完整的模板声明,并且在函数名前使用MyContainer<T>::限定符。

成员函数模板

类模板的成员函数本身也可以是模板,这称为成员函数模板

template <typename T>
class MyContainer {
    // ...其他成员

public:
    // 成员函数模板
    template <typename Func>
    void forEach(Func func) {
        for (size_t i = 0; i < count; ++i) {
            func(elements[i]);
        }
    }
    
    // 另一个成员函数模板
    template <typename U>
    void appendFrom(const MyContainer<U>& other) {
        for (size_t i = 0; i < other.size(); ++i) {
            // 需要U能转换为T
            push_back(static_cast<T>(other.at(i)));
        }
    }
};

使用成员函数模板:

int main() {
    MyContainer<int> intContainer;
    intContainer.push_back(1);
    intContainer.push_back(2);
    intContainer.push_back(3);
    
    // 使用forEach和lambda表达式打印元素
    intContainer.forEach([](const int& value) {
        std::cout << value << " ";
    });
    std::cout << std::endl;
    
    // 从double容器向int容器添加元素
    MyContainer<double> doubleContainer;
    doubleContainer.push_back(1.1);
    doubleContainer.push_back(2.2);
    
    intContainer.appendFrom(doubleContainer);  // 将double转换为int
    
    return 0;
}

类模板的特化

类模板可以通过特化为特定类型提供专门的实现。这在类型有特殊需求或可以优化时非常有用。

全特化

类模板的全特化为特定类型提供完全不同的实现:

// 主模板
template <typename T>
class Storage {
private:
    T data;
public:
    Storage(const T& value) : data(value) {}
    void print() const {
        std::cout << "Generic Storage: " << data << std::endl;
    }
    T& getData() { return data; }
};

// 为bool类型的全特化
template <>
class Storage<bool> {
private:
    unsigned char data;  // 使用一个字节存储多个bool值
    static const unsigned char mask = 1;  // 掩码
public:
    Storage(bool value) : data(value ? mask : 0) {}
    void print() const {
        std::cout << "Bool Storage: " << (data ? "true" : "false") << std::endl;
    }
    bool getData() { return data & mask; }
    void setData(bool value) { 
        if (value)
            data |= mask;  // 设置位
        else
            data &= ~mask;  // 清除位
    }
};

使用特化的类模板:

int main() {
    Storage<int> intStorage(42);
    intStorage.print();  // 使用通用版本
    
    Storage<bool> boolStorage(true);
    boolStorage.print();  // 使用bool特化版本
    
    boolStorage.setData(false);
    std::cout << "Changed value: " << (boolStorage.getData() ? "true" : "false") << std::endl;
    
    return 0;
}

偏特化

类模板的偏特化允许为模板参数的某个子集提供特化:

// 主模板
template <typename T, typename U>
class Pair {
private:
    T first;
    U second;
public:
    Pair(const T& t, const U& u) : first(t), second(u) {}
    void print() const {
        std::cout << "Generic Pair: " << first << ", " << second << std::endl;
    }
};

// 两个类型相同的偏特化
template <typename T>
class Pair<T, T> {
private:
    T first;
    T second;
public:
    Pair(const T& t1, const T& t2) : first(t1), second(t2) {}
    void print() const {
        std::cout << "Same-type Pair: " << first << ", " << second << std::endl;
    }
    bool areEqual() const { return first == second; }
};

// 指针类型的偏特化
template <typename T, typename U>
class Pair<T*, U*> {
private:
    T* first;
    U* second;
public:
    Pair(T* t, U* u) : first(t), second(u) {}
    void print() const {
        std::cout << "Pointer Pair: " << *first << ", " << *second << std::endl;
    }
};

使用偏特化的类模板:

int main() {
    // 使用通用版本
    Pair<int, double> p1(42, 3.14);
    p1.print();
    
    // 使用相同类型的偏特化
    Pair<int, int> p2(10, 20);
    p2.print();
    std::cout << "Are equal: " << (p2.areEqual() ? "yes" : "no") << std::endl;
    
    // 使用指针类型的偏特化
    int x = 100, y = 200;
    Pair<int, int> p3(&x, &y);
    p3.print();
    
    return 0;
}

默认模板参数

类模板可以为模板参数指定默认值,简化模板的使用:

template <typename T, 
          typename Container = std::vector<T>, 
          typename Compare = std::less<T>>
class PriorityQueue {
private:
    Container data;
    Compare comp;
    
public:
    PriorityQueue() = default;
    
    void push(const T& value) {
        data.push_back(value);
        std::push_heap(data.begin(), data.end(), comp);
    }
    
    T pop() {
        T top = data.front();
        std::pop_heap(data.begin(), data.end(), comp);
        data.pop_back();
        return top;
    }
    
    const T& top() const {
        return data.front();
    }
    
    bool empty() const {
        return data.empty();
    }
    
    size_t size() const {
        return data.size();
    }
};

使用默认模板参数:

int main() {
    // 使用默认参数:vector<int>容器和less<int>比较器
    PriorityQueue<int> minHeap;
    minHeap.push(3);
    minHeap.push(1);
    minHeap.push(4);
    
    while (!minHeap.empty()) {
        std::cout << minHeap.pop() << " "; // 输出:1 3 4
    }
    std::cout << std::endl;
    
    // 使用自定义比较器:最大堆
    PriorityQueue<int, std::vector<int>, std::greater<int>> maxHeap;
    maxHeap.push(3);
    maxHeap.push(1);
    maxHeap.push(4);
    
    while (!maxHeap.empty()) {
        std::cout << maxHeap.pop() << " "; // 输出:4 3 1
    }
    std::cout << std::endl;
    
    return 0;
}

模板的继承与组合

模板类的继承

模板类可以继承自非模板类、其他模板类或者自身的特化:

// 基类模板
template <typename T>
class Container {
protected:
    T* data;
    size_t size;
    
public:
    Container(size_t s = 0) : size(s), data(s > 0 ? new T[s] : nullptr) {}
    virtual ~Container() { delete[] data; }
    
    size_t getSize() const { return size; }
    virtual void print() const = 0;
};

// 派生类模板从基类模板继承
template <typename T>
class Array : public Container<T> {
public:
    Array(size_t s) : Container<T>(s) {}
    
    T& operator[](size_t index) {
        if (index >= this->size) throw std::out_of_range("Index out of bounds");
        return this->data[index];
    }
    
    void print() const override {
        std::cout << "Array: ";
        for (size_t i = 0; i < this->size; ++i) {
            std::cout << this->data[i] << " ";
        }
        std::cout << std::endl;
    }
};

// 模板类派生自非模板类
class Shape {
protected:
    std::string name;
public:
    Shape(const std::string& n) : name(n) {}
    virtual ~Shape() = default;
    virtual double area() const = 0;
    const std::string& getName() const { return name; }
};

template <typename T>
class Circle : public Shape {
private:
    T radius;
public:
    Circle(const std::string& name, T r) : Shape(name), radius(r) {}
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
};

使用继承的模板类:

int main() {
    // Array继承自Container
    Array<int> intArray(5);
    for (size_t i = 0; i < intArray.getSize(); ++i) {
        intArray[i] = i * 10;
    }
    intArray.print();
    
    // Circle继承自Shape
    Circle<double> circle("My Circle", 2.5);
    std::cout << circle.getName() << " has area: " << circle.area() << std::endl;
    
    return 0;
}

模板与组合

模板类也经常使用组合(而非继承)来实现代码复用:

// 使用组合的Stack模板
template <typename T, typename Container = std::vector<T>>
class Stack {
private:
    Container container;  // 组合一个容器
    
public:
    void push(const T& value) {
        container.push_back(value);
    }
    
    void pop() {
        if (empty()) throw std::underflow_error("Stack is empty");
        container.pop_back();
    }
    
    const T& top() const {
        if (empty()) throw std::underflow_error("Stack is empty");
        return container.back();
    }
    
    bool empty() const {
        return container.empty();
    }
    
    size_t size() const {
        return container.size();
    }
};

使用组合的模板类:

int main() {
    // 默认使用vector作为底层容器
    Stack<int> stack1;
    stack1.push(1);
    stack1.push(2);
    stack1.push(3);
    
    std::cout << "Stack with vector: ";
    while (!stack1.empty()) {
        std::cout << stack1.top() << " ";
        stack1.pop();
    }
    std::cout << std::endl;
    
    // 使用deque作为底层容器
    Stack<int, std::deque<int>> stack2;
    stack2.push(4);
    stack2.push(5);
    stack2.push(6);
    
    std::cout << "Stack with deque: ";
    while (!stack2.empty()) {
        std::cout << stack2.top() << " ";
        stack2.pop();
    }
    std::cout << std::endl;
    
    return 0;
}

类模板的嵌套

类模板可以包含嵌套的类、结构体或者枚举,这些嵌套类型也可以依赖外部模板参数:

template <typename T>
class OuterTemplate {
public:
    // 嵌套的类模板
    template <typename U>
    class NestedTemplate {
    private:
        T outerValue;
        U innerValue;
        
    public:
        NestedTemplate(const T& t, const U& u) : outerValue(t), innerValue(u) {}
        
        void print() const {
            std::cout << "Outer value: " << outerValue 
                      << ", Inner value: " << innerValue << std::endl;
        }
    };
    
    // 依赖于T的嵌套类
    class NestedClass {
    private:
        T value;
        
    public:
        NestedClass(const T& v) : value(v) {}
        
        void print() const {
            std::cout << "Nested value: " << value << std::endl;
        }
    };
    
    // 嵌套枚举(C++11起可以指定底层类型)
    enum class Status : int {
        Success = 0,
        Failure = 1,
        Pending = 2
    };
};

使用嵌套类模板:

int main() {
    // 使用嵌套的类模板
    OuterTemplate<int>::NestedTemplate<std::string> nested(42, "Hello");
    nested.print();
    
    // 使用依赖于外部模板参数的嵌套类
    OuterTemplate<double>::NestedClass nestedObj(3.14);
    nestedObj.print();
    
    // 使用嵌套枚举
    auto status = OuterTemplate<int>::Status::Success;
    std::cout << "Status value: " << static_cast<int>(status) << std::endl;
    
    return 0;
}

类型萃取和SFINAE

类模板在实现通用代码时,常常需要对不同类型做特殊处理。类型萃取(Type Traits)和SFINAE(Substitution Failure Is Not An Error)是用于此目的的重要技术。

类型萃取

类型萃取允许我们在编译时检查和修改类型特性:

#include <type_traits>

// 使用类型萃取实现通用容器
template <typename T>
class SafeContainer {
private:
    std::vector<T> data;
    
    // 使用SFINAE选择合适的初始化函数
    template <typename U = T>
    typename std::enable_if<std::is_default_constructible<U>::value>::type
    initialize(size_t size) {
        data.resize(size);  // T可以默认构造,直接调用resize
    }
    
    template <typename U = T>
    typename std::enable_if<!std::is_default_constructible<U>::value>::type
    initialize(size_t size) {
        // T不可默认构造,不调整大小,仅预留空间
        data.reserve(size);
    }
    
public:
    SafeContainer(size_t size = 0) {
        initialize<T>(size);
    }
    
    void add(const T& value) {
        data.push_back(value);
    }
    
    size_t size() const {
        return data.size();
    }
    
    // 基于类型特性提供特定功能
    template <typename U = T>
    typename std::enable_if<std::is_arithmetic<U>::value, U>::type
    sum() const {
        U result = U();
        for (const auto& item : data) {
            result += item;
        }
        return result;
    }
};

使用结合类型萃取的容器:

class NonDefaultConstructible {
private:
    int value;
public:
    NonDefaultConstructible(int v) : value(v) {}
    // 没有默认构造函数
};

int main() {
    // 使用可默认构造的类型
    SafeContainer<int> intContainer(5);
    intContainer.add(10);
    intContainer.add(20);
    
    std::cout << "Int container size: " << intContainer.size() << std::endl;
    std::cout << "Int container sum: " << intContainer.sum() << std::endl;
    
    // 使用不可默认构造的类型
    SafeContainer<NonDefaultConstructible> customContainer;
    customContainer.add(NonDefaultConstructible(5));
    customContainer.add(NonDefaultConstructible(10));
    
    std::cout << "Custom container size: " << customContainer.size() << std::endl;
    // customContainer.sum() 会导致编译错误,因为NonDefaultConstructible不是算术类型
    
    return 0;
}

SFINAE技术

SFINAE允许我们根据类型特性提供不同的模板特化:

#include <iostream>
#include <type_traits>
#include <vector>
#include <list>

// 检测容器是否有随机访问迭代器
template <typename Container>
class ContainerTraits {
private:
    // 测试函数,只有当迭代器为随机访问迭代器时有效
    template <typename C>
    static constexpr auto test(int) 
        -> decltype(
            typename C::iterator() += 1,  // 随机访问迭代器支持+=操作
            std::true_type{}
           );
           
    // 匹配任意类型的后备函数
    template <typename>
    static constexpr std::false_type test(...);
    
public:
    // value为true表示容器有随机访问迭代器
    static constexpr bool has_random_access = decltype(test<Container>(0))::value;
};

// 根据容器特性选择最优的算法
template <typename Container>
void process(Container& container) {
    if constexpr (ContainerTraits<Container>::has_random_access) {
        std::cout << "Using fast algorithm for random access container" << std::endl;
        // 实现依赖随机访问的快速算法
    } else {
        std::cout << "Using general algorithm for sequential access container" << std::endl;
        // 实现适用于顺序访问的通用算法
    }
}

使用SFINAE的容器处理:

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::list<int> lst = {1, 2, 3, 4, 5};
    
    std::cout << "Vector has random access: " 
              << (ContainerTraits<std::vector<int>>::has_random_access ? "yes" : "no") << std::endl;
    std::cout << "List has random access: " 
              << (ContainerTraits<std::list<int>>::has_random_access ? "yes" : "no") << std::endl;
    
    process(vec);  // 使用快速算法
    process(lst);  // 使用一般算法
    
    return 0;
}

可变参数模板类

C++11引入了可变参数模板,允许接受任意数量和类型的模板参数:

// 可变参数类模板
template <typename... Types>
class Tuple;

// 基本情况:空元组
template <>
class Tuple<> {
public:
    static constexpr size_t size = 0;
    
    void print() const {
        std::cout << "()" << std::endl;
    }
};

// 递归情况:首元素 + 剩余元素元组
template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : private Tuple<Tail...> {
private:
    Head head;
    
    // 基类类型别名
    using Base = Tuple<Tail...>;
    
public:
    static constexpr size_t size = 1 + Base::size;
    
    // 构造函数
    Tuple(const Head& h, const Tail&... tail)
        : Base(tail...), head(h) {}
    
    // 获取首元素
    const Head& getHead() const {
        return head;
    }
    
    // 获取尾元组
    const Base& getTail() const {
        return *this;
    }
    
    // 递归打印元组内容
    void print() const {
        std::cout << "(" << head;
        printRest();
        std::cout << ")" << std::endl;
    }
    
private:
    void printRest() const {
        const Base& tail = getTail();
        // 检查尾元组是否为空
        if constexpr (Base::size > 0) {
            std::cout << ", ";
            // 访问尾元组的getHead()方法
            std::cout << tail.getHead();
            // 递归处理剩余元素
            tail.printRest();
        }
    }
};

使用可变参数类模板:

int main() {
    // 创建不同类型的元组
    Tuple<int, double, std::string> t1(42, 3.14, "hello");
    
    std::cout << "Tuple t1: ";
    t1.print();
    std::cout << "Size of t1: " << decltype(t1)::size << std::endl;
    
    // 创建单元素元组
    Tuple<char> t2('A');
    
    std::cout << "Tuple t2: ";
    t2.print();
    
    // 创建空元组
    Tuple<> t3;
    
    std::cout << "Tuple t3: ";
    t3.print();
    
    return 0;
}

类模板实例化控制

类模板的每次实例化都会生成代码,这可能导致代码膨胀。通过显式实例化和外部模板声明,可以控制实例化的位置和次数:

// 在头文件中声明模板
// mycontainer.h
template <typename T>
class MyContainer {
private:
    std::vector<T> data;
    
public:
    void add(const T& value);
    const T& get(size_t index) const;
    size_t size() const;
};

// 实现模板
// mycontainer.cpp
template <typename T>
void MyContainer<T>::add(const T& value) {
    data.push_back(value);
}

template <typename T>
const T& MyContainer<T>::get(size_t index) const {
    return data.at(index);
}

template <typename T>
size_t MyContainer<T>::size() const {
    return data.size();
}

// 显式实例化常用类型,减少代码重复
template class MyContainer<int>;
template class MyContainer<double>;
template class MyContainer<std::string>;

// 在其他文件中使用外部模板声明
// main.cpp
extern template class MyContainer<int>;
extern template class MyContainer<double>;

int main() {
    MyContainer<int> intContainer;
    intContainer.add(42);
    
    MyContainer<double> doubleContainer;
    doubleContainer.add(3.14);
    
    // 这将导致新的实例化,因为没有显式实例化
    MyContainer<char> charContainer;
    charContainer.add('A');
    
    return 0;
}

实际应用案例

让我们看一些类模板的实际应用案例:

通用智能指针

实现一个简单的智能指针模板类:

#include <iostream>
#include <cstddef>

// 定义删除器类型
template <typename T>
struct DefaultDeleter {
    void operator()(T* ptr) const {
        delete ptr;
    }
};

template <typename T>
struct ArrayDeleter {
    void operator()(T* ptr) const {
        delete[] ptr;
    }
};

// 自定义智能指针模板
template <typename T, typename Deleter = DefaultDeleter<T>>
class UniquePtr {
private:
    T* ptr;
    Deleter deleter;
    
    // 禁止拷贝
    UniquePtr(const UniquePtr&) = delete;
    UniquePtr& operator=(const UniquePtr&) = delete;
    
public:
    // 构造函数
    explicit UniquePtr(T* p = nullptr, Deleter d = Deleter())
        : ptr(p), deleter(std::move(d)) {}
        
    // 移动构造函数
    UniquePtr(UniquePtr&& other) noexcept
        : ptr(other.ptr), deleter(std::move(other.deleter)) {
        other.ptr = nullptr;
    }
    
    // 移动赋值运算符
    UniquePtr& operator=(UniquePtr&& other) noexcept {
        if (this != &other) {
            reset();
            ptr = other.ptr;
            deleter = std::move(other.deleter);
            other.ptr = nullptr;
        }
        return *this;
    }
    
    // 析构函数
    ~UniquePtr() {
        reset();
    }
    
    // 释放资源
    void reset(T* p = nullptr) {
        if (ptr != nullptr) {
            deleter(ptr);
        }
        ptr = p;
    }
    
    // 放弃所有权
    T* release() {
        T* temp = ptr;
        ptr = nullptr;
        return temp;
    }
    
    // 访问指针
    T* get() const {
        return ptr;
    }
    
    // 解引用操作符
    T& operator*() const {
        return *ptr;
    }
    
    // 成员访问操作符
    T* operator->() const {
        return ptr;
    }
    
    // 布尔转换
    explicit operator bool() const {
        return ptr != nullptr;
    }
    
    // 交换
    void swap(UniquePtr& other) noexcept {
        std::swap(ptr, other.ptr);
        std::swap(deleter, other.deleter);
    }
};

// 为数组类型特化make_unique
template <typename T, typename... Args>
UniquePtr<T> make_unique(Args&&... args) {
    return UniquePtr<T>(new T(std::forward<Args>(args)...));
}

template <typename T>
UniquePtr<T, ArrayDeleter<T>> make_unique_array(size_t size) {
    return UniquePtr<T, ArrayDeleter<T>>(new T[size]);
}

使用自定义智能指针:

class Resource {
private:
    std::string name;
public:
    Resource(const std::string& n = "Unnamed") : name(n) {
        std::cout << "Resource " << name << " created" << std::endl;
    }
    
    ~Resource() {
        std::cout << "Resource " << name << " destroyed" << std::endl;
    }
    
    void use() const {
        std::cout << "Using resource " << name << std::endl;
    }
};

int main() {
    // 创建智能指针
    UniquePtr<Resource> res1(new Resource("First"));
    
    // 使用make_unique
    auto res2 = make_unique<Resource>("Second");
    
    // 使用资源
    res1->use();
    (*res2).use();
    
    // 移动所有权
    UniquePtr<Resource> res3 = std::move(res1);
    
    // res1现在为空
    if (!res1) {
        std::cout << "res1 is empty" << std::endl;
    }
    
    // res3持有原res1的资源
    if (res3) {
        res3->use();
    }
    
    // 数组版本
    auto numbers = make_unique_array<int>(5);
    
    // 函数结束时,所有智能指针销毁,释放资源
    return 0;
}

泛型事件系统

实现一个简单的类型安全事件系统:

#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <vector>
#include <memory>
#include <any>

// 事件基类
class EventBase {};

// 泛型事件类
template <typename... Args>
class Event : public EventBase {
public:
    using HandlerFunc = std::function<void(Args...)>;
    using HandlerId = size_t;
    
private:
    std::unordered_map<HandlerId, HandlerFunc> handlers;
    HandlerId nextId = 0;
    
public:
    // 注册处理器
    HandlerId addHandler(HandlerFunc handler) {
        HandlerId id = nextId++;
        handlers[id] = std::move(handler);
        return id;
    }
    
    // 移除处理器
    void removeHandler(HandlerId id) {
        handlers.erase(id);
    }
    
    // 触发事件
    void trigger(Args... args) const {
        for (const auto& [id, handler] : handlers) {
            handler(args...);
        }
    }
    
    // 获取处理器数量
    size_t handlerCount() const {
        return handlers.size();
    }
};

// 事件分发器
class EventDispatcher {
private:
    std::unordered_map<std::string, std::shared_ptr<EventBase>> events;
    
public:
    // 获取事件(如果不存在则创建)
    template <typename... Args>
    Event<Args...>& getEvent(const std::string& name) {
        auto it = events.find(name);
        if (it == events.end()) {
            auto event = std::make_shared<Event<Args...>>();
            events[name] = event;
            return *event;
        }
        
        // 尝试将事件转换为正确的类型
        auto* typedEvent = dynamic_cast<Event<Args...>*>(it->second.get());
        if (!typedEvent) {
            throw std::runtime_error("Event type mismatch");
        }
        
        return *typedEvent;
    }
    
    // 触发事件
    template <typename... Args>
    void dispatchEvent(const std::string& name, Args... args) {
        auto& event = getEvent<Args...>(name);
        event.trigger(args...);
    }
};

使用泛型事件系统:

class Player {
private:
    std::string name;
    int health;
    
public:
    Player(const std::string& n, int h) : name(n), health(h) {}
    
    void takeDamage(int amount) {
        health -= amount;
        std::cout << name << " takes " << amount << " damage. Health: " << health << std::endl;
    }
    
    void heal(int amount) {
        health += amount;
        std::cout << name << " heals for " << amount << ". Health: " << health << std::endl;
    }
    
    const std::string& getName() const { return name; }
    int getHealth() const { return health; }
};

int main() {
    EventDispatcher dispatcher;
    
    // 定义并注册各种事件
    using DamageEvent = Event<Player&, int>;
    using HealEvent = Event<Player&, int>;
    using GameOverEvent = Event<const std::string&>;
    
    // 创建玩家
    Player player("Hero", 100);
    
    // 注册伤害事件处理器
    dispatcher.getEvent<Player&, int>("damage").addHandler(
        [](Player& p, int amount) {
            p.takeDamage(amount);
            std::cout << "Damage event handled!" << std::endl;
        }
    );
    
    // 注册治疗事件处理器
    dispatcher.getEvent<Player&, int>("heal").addHandler(
        [](Player& p, int amount) {
            p.heal(amount);
            std::cout << "Heal event handled!" << std::endl;
        }
    );
    
    // 注册游戏结束事件处理器
    dispatcher.getEvent<const std::string&>("gameOver").addHandler(
        [](const std::string& message) {
            std::cout << "Game over: " << message << std::endl;
        }
    );
    
    // 触发一些事件
    dispatcher.dispatchEvent("damage", player, 30);
    dispatcher.dispatchEvent("heal", player, 15);
    dispatcher.dispatchEvent("damage", player, 95);
    
    // 检查玩家状态
    if (player.getHealth() <= 0) {
        dispatcher.dispatchEvent("gameOver", "Player has been defeated!");
    }
    
    return 0;
}

类模板的最佳实践

接口设计

  1. 简洁明了的模板参数:只使用必要的模板参数,并为常用场景提供默认参数。
  2. 分离接口与实现:将模板的声明和实现分开,提高代码可读性。
  3. 提供清晰的文档:注明模板参数的预期类型和约束条件。
// 良好设计的模板类
template <typename T, 
          typename Allocator = std::allocator<T>, 
          typename Compare = std::less<T>>
class SortedContainer {
public:
    // 类型别名提高可读性
    using value_type = T;
    using allocator_type = Allocator;
    using compare_type = Compare;
    using size_type = std::size_t;
    
    // 构造函数
    explicit SortedContainer(const Compare& comp = Compare{});
    
    // 主要接口方法
    void insert(const T& value);
    bool contains(const T& value) const;
    size_type size() const;
    bool empty() const;
    
    // 迭代器
    using iterator = /* ... */;
    using const_iterator = /* ... */;
    
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
    const_iterator cbegin() const;
    const_iterator cend() const;
    
    // 对于每个必要的操作都提供文档
    /**
     * 插入值并保持容器排序
     * @param value 要插入的值
     * @complexity O(log n) for lookup, O(n) for insertion
     */
    void insert(const T& value);
    
    // ... 其他实现 ...
};

模板约束

使用适当的技术确保模板参数满足必要的要求:

// 使用C++20概念
template <typename T>
concept Sortable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
    { a = b } -> std::same_as<T&>;
};

template <Sortable T>
class SortedVector {
    // 实现...
};

// 在C++17及更早版本中使用SFINAE
template <typename T, 
          typename = std::enable_if_t<
              std::is_copy_assignable_v<T> &&
              std::is_copy_constructible_v<T> &&
              std::is_default_constructible_v<T>
          >>
class SafeContainer {
    // 实现...
};

避免常见问题

  1. 过多的模板参数:限制模板参数的数量,使类易于使用。
  2. 不必要的依赖:避免在模板中包含不依赖于模板参数的代码。
  3. 编译时间膨胀:使用技术如显式实例化和外部模板声明减少代码重复。
// 不好的设计:过多的模板参数
template <typename T, 
          typename U, 
          typename V, 
          typename W, 
          typename Allocator = std::allocator<T>>
class OverTemplated { /* ... */ };

// 更好的设计:使用嵌套或组合
template <typename Key, typename Value>
class Dictionary {
private:
    struct Entry {
        Key key;
        Value value;
    };
    
    std::vector<Entry> entries;
    
public:
    // 更简洁的接口
};

// 不好的设计:不依赖模板参数的代码
template <typename T>
class Logger {
public:
    void log(const std::string& message) {
        // 这个方法不使用T
        std::cout << message << std::endl;
    }
    
    void logValue(const T& value) {
        std::cout << value << std::endl;
    }
};

// 更好的设计:分离不依赖模板参数的代码
class LoggerBase {
public:
    void log(const std::string& message) {
        std::cout << message << std::endl;
    }
};

template <typename T>
class TypedLogger : public LoggerBase {
public:
    void logValue(const T& value) {
        std::cout << value << std::endl;
    }
};

总结

类模板是C++泛型编程的核心组件之一,它允许我们创建能够处理各种数据类型的通用类。通过类模板,我们可以实现代码复用、类型安全和高效的容器和算法。

本文中,我们探讨了:

  1. 类模板的基本语法:定义和实例化类模板的方法
  2. 类模板的成员函数:在类内和类外定义成员函数
  3. 类模板的特化:为特定类型提供专门实现的全特化和偏特化
  4. 默认模板参数:简化模板使用的方法
  5. 模板的继承与组合:通过继承和组合使用模板
  6. 类模板的嵌套:在模板中定义嵌套类型
  7. 类型萃取和SFINAE:根据类型特性提供不同实现
  8. 可变参数模板类:创建接受任意数量参数的模板
  9. 类模板实例化控制:管理模板代码膨胀的技术
  10. 实际应用案例:智能指针和事件系统实现
  11. 最佳实践:设计良好模板类的指导原则

掌握类模板是成为高级C++程序员的关键一步。随着我们继续探索模板编程,接下来我们将深入学习模板特化技术,这将使我们能够为特定类型提供更加高效或专门化的实现。

参考资源

  • C++ Reference
  • 《C++ Templates: The Complete Guide》by David Vandevoorde and Nicolai M. Josuttis
  • 《Modern C++ Design》by Andrei Alexandrescu
  • 《Effective Modern C++》by Scott Meyers
  • 《C++ Core Guidelines》by Bjarne Stroustrup and Herb Sutter

在这里插入图片描述


这是我C++学习之旅系列的第三十三篇技术文章。查看完整系列目录了解更多内容。

如有任何问题或建议,欢迎在评论区留言交流!

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superior tigre

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值