【C++模板与泛型编程】类模板成员

目录

一、类模板成员函数

1.1 类模板成员函数的定义与实例化

1.2 成员函数的实例化规则

二、非类型形参的模板实参

2.1 非类型参数的基本语法 

2.2 非类型参数的限制

三、类模板中的友元声明

3.1 普通友元声明 

3.2 模板友元声明  

四、Queue 和 QueueItem 的友元声明示例

4.1 Queue 和 QueueItem 的实现 

五、成员模板

5.1 成员函数模板 

5.2 嵌套类模板 

六、完整的 Queue 类实现

6.1 完整的 Queue 类代码

6.2 使用示例

七、类模板中的 static 成员

7.1 static 成员的基本概念

7.2 static 成员函数

7.3 static 成员与模板特化

八、总结与最佳实践

8.1 类模板成员的关键特性

8.2 最佳实践建议


在 C++ 泛型编程中,类模板是构建通用数据结构和算法的核心工具。类模板不仅允许我们定义与类型无关的类,还支持丰富的成员特性,如成员函数、非类型参数、友元声明、成员模板和静态成员等。

一、类模板成员函数

1.1 类模板成员函数的定义与实例化

类模板的成员函数本身也是模板,需要在使用时根据上下文实例化。成员函数可以在类体内定义(隐式内联),也可以在类体外定义。

示例:类体内定义成员函数

template<typename T>
class Stack {
private:
    std::vector<T> elements;
public:
    // 类体内定义成员函数
    void push(const T& value) {
        elements.push_back(value);
    }
    
    T pop() {
        if (elements.empty()) throw std::out_of_range("Stack is empty");
        T top = elements.back();
        elements.pop_back();
        return top;
    }
};

示例:类体外定义成员函数

template<typename T>
class Stack {
private:
    std::vector<T> elements;
public:
    void push(const T& value);
    T pop();
};

// 类体外定义成员函数
template<typename T>
void Stack<T>::push(const T& value) {
    elements.push_back(value);
}

template<typename T>
T Stack<T>::pop() {
    if (elements.empty()) throw std::out_of_range("Stack is empty");
    T top = elements.back();
    elements.pop_back();
    return top;
}

1.2 成员函数的实例化规则

类模板的成员函数只有在被调用或取地址时才会被实例化。这种延迟实例化机制可以避免不必要的代码生成。 

template<typename T>
class Container {
public:
    void printType() {
        std::cout << typeid(T).name() << std::endl;
    }
    
    void unusedFunction() {
        // 包含无效代码,但未被调用时不会实例化
        T::undefined_member; // 仅在调用时触发编译错误
    }
};

int main() {
    Container<int> c;
    c.printType(); // 实例化printType<int>()
    // c.unusedFunction(); // 若取消注释,将触发编译错误
    return 0;
}

二、非类型形参的模板实参

类模板不仅可以接受类型参数,还可以接受非类型参数(如整数、指针、引用等)。非类型参数在编译时必须是常量表达式。

2.1 非类型参数的基本语法 

template<typename T, size_t N>
class Array {
private:
    T data[N];
public:
    T& operator[](size_t index) { return data[index]; }
    const T& operator[](size_t index) const { return data[index]; }
    size_t size() const { return N; }
};

// 使用非类型参数实例化
Array<int, 10> intArray; // 创建包含10个整数的数组
Array<double, 5> doubleArray; // 创建包含5个双精度浮点数的数组

2.2 非类型参数的限制

非类型参数必须满足以下条件:

  • 整数类型(包括枚举)
  • 指针或引用类型
  • 成员指针类型
  • 字面类型(C++20 起) 
// 错误示例:非类型参数不能是浮点类型
// template<typename T, double N> class InvalidArray {}; // 编译错误

// 正确示例:非类型参数可以是指针
template<typename T, T* ptr>
class PointerWrapper {
    // ...
};

int globalVar = 42;
PointerWrapper<int, &globalVar> wrapper;

三、类模板中的友元声明

类模板中的友元声明允许外部类、函数或其他模板访问其私有成员。友元声明可以分为三类:普通友元、模板友元(特定实例化或所有实例化)和成员模板友元

3.1 普通友元声明 

template<typename T>
class Container {
private:
    T value;
public:
    Container(const T& val) : value(val) {}
    
    // 普通友元函数
    friend void printValue(const Container<T>& obj) {
        std::cout << "Value: " << obj.value << std::endl;
    }
};

// 使用友元函数
int main() {
    Container<int> c(42);
    printValue(c); // 输出: Value: 42
    return 0;
}

 

3.2 模板友元声明  

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

// 模板函数声明
template<typename T>
void printContainer(const Container<T>& obj);

template<typename T>
class Container {
private:
    T value;
public:
    Container(const T& val) : value(val) {}
    
    // 特定实例化的模板友元
    friend void printContainer<T>(const Container<T>& obj);
    
    // 所有实例化的模板友元
    template<typename U>
    friend void globalFriendFunction(const Container<U>& obj);
};

// 模板友元函数的定义
template<typename T>
void printContainer(const Container<T>& obj) {
    std::cout << "Template friend: " << obj.value << std::endl;
}

template<typename U>
void globalFriendFunction(const Container<U>& obj) {
    std::cout << "Global friend: " << obj.value << std::endl;
}

四、Queue 和 QueueItem 的友元声明示例

队列(Queue)是一种常见的数据结构,通常由节点(QueueItem)组成。通过友元声明,Queue 可以访问 QueueItem 的私有成员。

4.1 Queue 和 QueueItem 的实现 

// QueueItem类:表示队列中的节点
template<typename T>
class QueueItem;

// Queue类:表示队列
template<typename T>
class Queue {
private:
    QueueItem<T>* head;
    QueueItem<T>* tail;
public:
    Queue() : head(nullptr), tail(nullptr) {}
    ~Queue();
    
    void push(const T& value);
    void pop();
    bool empty() const { return head == nullptr; }
    T& front() { return head->value; }
    const T& front() const { return head->value; }
    
    // 声明QueueItem为友元类,允许访问其私有成员
    friend class QueueItem<T>;
};

// QueueItem类的实现
template<typename T>
class QueueItem {
private:
    T value;
    QueueItem* next;
    
    // 私有构造函数,只能由Queue创建
    QueueItem(const T& val) : value(val), next(nullptr) {}
    
    // 私有析构函数,只能由Queue销毁
    ~QueueItem() = default;
    
    // 声明Queue为友元类,允许访问其私有成员
    friend class Queue<T>;
};

// Queue类成员函数的实现
template<typename T>
Queue<T>::~Queue() {
    while (!empty()) {
        pop();
    }
}

template<typename T>
void Queue<T>::push(const T& value) {
    QueueItem<T>* newItem = new QueueItem<T>(value);
    if (empty()) {
        head = tail = newItem;
    } else {
        tail->next = newItem;
        tail = newItem;
    }
}

template<typename T>
void Queue<T>::pop() {
    if (empty()) return;
    QueueItem<T>* oldHead = head;
    head = head->next;
    delete oldHead;
    if (head == nullptr) {
        tail = nullptr;
    }
}

五、成员模板

类模板的成员本身也可以是模板,称为成员模板。成员模板可以是成员函数模板或嵌套类模板。

5.1 成员函数模板 

#include <iostream>

template<typename T>
class Container {
private:
    T value;
public:
    // 声明所有Container实例化类型为友元
    template<typename U>
    friend class Container; // 友元声明

    Container(const T& val) : value(val) {}

    // 成员函数模板(转换构造函数)
    template<typename U>
    Container(const Container<U>& other) : value(other.value) { // 现在可以访问other.value
        std::cout << "Template conversion constructor" << std::endl;
    }

    T getValue() const { return value; }
};

int main() {
    Container<double> d(3.14);
    Container<int> i(d); // 调用模板转换构造函数
    std::cout << i.getValue() << std::endl; // 输出: 3
    return 0;
}

 

5.2 嵌套类模板 

template<typename T>
class Outer {
private:
    T value;
public:
    Outer(const T& val) : value(val) {}
    
    // 嵌套类模板
    template<typename U>
    class Inner {
    private:
        U innerValue;
    public:
        Inner(const U& val) : innerValue(val) {}
        void printOuterValue(const Outer<T>& outer) {
            std::cout << "Outer value: " << outer.value << std::endl;
        }
    };
};

// 使用嵌套类模板
int main() {
    Outer<int> outer(42);
    Outer<int>::Inner<double> inner(3.14);
    inner.printOuterValue(outer); // 输出: Outer value: 42
    return 0;
}

 

六、完整的 Queue 类实现

6.1 完整的 Queue 类代码

下面是一个完整的队列类实现,包含了前面讨论的各种特性: 

#ifndef QUEUE_H
#define QUEUE_H

#include <iostream>
#include <stdexcept>

// 前置声明
template <typename T> class Queue;
template <typename T> std::ostream& operator<<(std::ostream& os, const Queue<T>& q);

// QueueItem类模板
template <typename T>
class QueueItem {
private:
    T data;
    QueueItem* next;

    // 构造函数设为私有,只能由Queue创建
    QueueItem(const T& val) : data(val), next(nullptr) {}

    // 友元声明
    friend class Queue<T>;
    friend std::ostream& operator<< <T>(std::ostream& os, const Queue<T>& q);
};

// Queue类模板
template <typename T>
class Queue {
private:
    QueueItem<T>* head;
    QueueItem<T>* tail;

public:
    // 构造函数
    Queue() : head(nullptr), tail(nullptr) {}

    // 拷贝构造函数
    Queue(const Queue& other);

    // 析构函数
    ~Queue();

    // 赋值运算符
    Queue& operator=(const Queue& other);

    // 入队
    void push(const T& value);

    // 出队
    void pop();

    // 获取队首元素
    T& front();
    const T& front() const;

    // 判断队列是否为空
    bool empty() const;

    // 成员函数模板:从另一种类型的队列复制
    template <typename U>
    Queue& operator=(const Queue<U>& other);

    // 友元声明
    friend std::ostream& operator<< <T>(std::ostream& os, const Queue<T>& q);
};

// 拷贝构造函数的实现
template <typename T>
Queue<T>::Queue(const Queue& other) : head(nullptr), tail(nullptr) {
    for (QueueItem<T>* p = other.head; p != nullptr; p = p->next) {
        push(p->data);
    }
}

// 析构函数的实现
template <typename T>
Queue<T>::~Queue() {
    while (!empty()) {
        pop();
    }
}

// 赋值运算符的实现
template <typename T>
Queue<T>& Queue<T>::operator=(const Queue& other) {
    if (this != &other) {
        // 清空当前队列
        while (!empty()) {
            pop();
        }
        // 复制另一个队列的元素
        for (QueueItem<T>* p = other.head; p != nullptr; p = p->next) {
            push(p->data);
        }
    }
    return *this;
}

// 入队操作的实现
template <typename T>
void Queue<T>::push(const T& value) {
    QueueItem<T>* newItem = new QueueItem<T>(value);
    if (empty()) {
        head = tail = newItem;
    } else {
        tail->next = newItem;
        tail = newItem;
    }
}

// 出队操作的实现
template <typename T>
void Queue<T>::pop() {
    if (empty()) {
        throw std::underflow_error("Queue is empty");
    }
    QueueItem<T>* oldHead = head;
    head = head->next;
    if (head == nullptr) {
        tail = nullptr;
    }
    delete oldHead;
}

// 获取队首元素的实现
template <typename T>
T& Queue<T>::front() {
    if (empty()) {
        throw std::underflow_error("Queue is empty");
    }
    return head->data;
}

template <typename T>
const T& Queue<T>::front() const {
    if (empty()) {
        throw std::underflow_error("Queue is empty");
    }
    return head->data;
}

// 判断队列是否为空的实现
template <typename T>
bool Queue<T>::empty() const {
    return head == nullptr;
}

// 成员函数模板:从另一种类型的队列复制
template <typename T>
template <typename U>
Queue<T>& Queue<T>::operator=(const Queue<U>& other) {
    // 清空当前队列
    while (!empty()) {
        pop();
    }
    
    // 创建一个临时队列用于复制
    Queue<U> temp = other;
    
    // 从临时队列中取出元素并添加到当前队列
    while (!temp.empty()) {
        push(temp.front());  // 通过公共接口访问元素
        temp.pop();          // 通过公共接口访问元素
    }
    
    return *this;
}

// 友元函数:输出队列内容
template <typename T>
std::ostream& operator<<(std::ostream& os, const Queue<T>& q) {
    os << "Queue: ";
    for (QueueItem<T>* p = q.head; p != nullptr; p = p->next) {
        os << p->data << " ";
    }
    return os;
}

#endif // QUEUE_H

6.2 使用示例

下面是使用这个队列类的示例代码: 

#include <iostream>
#include "queue.h"

int main() {
    try {
        // 创建一个整数队列
        Queue<int> intQueue;
        
        // 入队操作
        intQueue.push(10);
        intQueue.push(20);
        intQueue.push(30);
        
        // 输出队列
        std::cout << intQueue << std::endl;
        
        // 出队操作
        intQueue.pop();
        std::cout << "After pop: " << intQueue << std::endl;
        
        // 获取队首元素
        std::cout << "Front element: " << intQueue.front() << std::endl;
        
        // 创建一个字符串队列
        Queue<std::string> stringQueue;
        stringQueue.push("Hello");
        stringQueue.push("World");
        std::cout << stringQueue << std::endl;
        
        // 使用成员函数模板进行类型转换
        Queue<double> doubleQueue;
        Queue<int> anotherIntQueue;
        anotherIntQueue.push(1);
        anotherIntQueue.push(2);
        anotherIntQueue.push(3);
        
        // 从int队列赋值到double队列
        doubleQueue = anotherIntQueue;
        std::cout << "Double queue: " << doubleQueue << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    
    return 0;
}

// ---------- Queue.h ----------
#ifndef QUEUE_H
#define QUEUE_H

#include <iostream>
#include <stdexcept>

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

// QueueItem类:表示队列中的节点
template<typename T>
class QueueItem {
private:
    T value;
    QueueItem* next;
    
    // 私有构造函数,只能由Queue创建
    explicit QueueItem(const T& val) : value(val), next(nullptr) {}
    
    // 声明Queue为友元类,允许访问其私有成员
    friend class Queue<T>;
    
    // 禁用拷贝构造和赋值运算符
    QueueItem(const QueueItem&) = delete;
    QueueItem& operator=(const QueueItem&) = delete;
};

// Queue类:表示队列
template<typename T>
class Queue {
private:
    QueueItem<T>* head;
    QueueItem<T>* tail;
    static size_t instanceCount; // 静态成员:记录实例数量
    
public:
    // 构造函数
    Queue() : head(nullptr), tail(nullptr) {
        ++instanceCount;
        std::cout << "Queue created. Total instances: " << instanceCount << std::endl;
    }
    
    // 拷贝构造函数
    Queue(const Queue& other);
    
    // 析构函数
    ~Queue() {
        clear();
        --instanceCount;
        std::cout << "Queue destroyed. Total instances: " << instanceCount << std::endl;
    }
    
    // 赋值运算符
    Queue& operator=(const Queue& other);
    
    // 元素操作
    void push(const T& value);
    void pop();
    T& front();
    const T& front() const;
    bool empty() const { return head == nullptr; }
    size_t size() const;
    void clear();
    
    // 静态成员函数
    static size_t getInstanceCount() { return instanceCount; }
    
    // 友元函数:输出队列内容
    friend std::ostream& operator<<(std::ostream& os, const Queue<T>& queue) {
        os << "Queue: ";
        QueueItem<T>* current = queue.head;
        while (current != nullptr) {
            os << current->value << " ";
            current = current->next;
        }
        return os;
    }
};

// 初始化静态成员
template<typename T>
size_t Queue<T>::instanceCount = 0;

// 拷贝构造函数的实现
template<typename T>
Queue<T>::Queue(const Queue& other) : head(nullptr), tail(nullptr) {
    ++instanceCount;
    std::cout << "Queue copied. Total instances: " << instanceCount << std::endl;
    
    QueueItem<T>* current = other.head;
    while (current != nullptr) {
        push(current->value);
        current = current->next;
    }
}

// 赋值运算符的实现
template<typename T>
Queue<T>& Queue<T>::operator=(const Queue& other) {
    if (this != &other) {
        clear();
        
        QueueItem<T>* current = other.head;
        while (current != nullptr) {
            push(current->value);
            current = current->next;
        }
    }
    return *this;
}

// push操作的实现
template<typename T>
void Queue<T>::push(const T& value) {
    QueueItem<T>* newItem = new QueueItem<T>(value);
    if (empty()) {
        head = tail = newItem;
    } else {
        tail->next = newItem;
        tail = newItem;
    }
}

// pop操作的实现
template<typename T>
void Queue<T>::pop() {
    if (empty()) {
        throw std::out_of_range("Queue is empty");
    }
    
    QueueItem<T>* oldHead = head;
    head = head->next;
    delete oldHead;
    
    if (head == nullptr) {
        tail = nullptr;
    }
}

// front操作的实现
template<typename T>
T& Queue<T>::front() {
    if (empty()) {
        throw std::out_of_range("Queue is empty");
    }
    return head->value;
}

template<typename T>
const T& Queue<T>::front() const {
    if (empty()) {
        throw std::out_of_range("Queue is empty");
    }
    return head->value;
}

// size操作的实现
template<typename T>
size_t Queue<T>::size() const {
    size_t count = 0;
    QueueItem<T>* current = head;
    while (current != nullptr) {
        ++count;
        current = current->next;
    }
    return count;
}

// clear操作的实现
template<typename T>
void Queue<T>::clear() {
    while (!empty()) {
        pop();
    }
}

#endif // QUEUE_H

七、类模板中的 static 成员

7.1 static 成员的基本概念

类模板中的 static 成员属于每个实例化的类,而不是类模板本身。每个不同的实例化类都有自己独立的 static 成员: 

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

// 静态成员的初始化
template <typename T>
int Counter<T>::count = 0;

int main() {
    Counter<int> c1, c2;
    std::cout << "Counter<int> count: " << Counter<int>::getCount() << std::endl;  // 输出2
    
    Counter<double> d1;
    std::cout << "Counter<double> count: " << Counter<double>::getCount() << std::endl;  // 输出1
    
    return 0;
}

7.2 static 成员函数

类模板中的 static 成员函数也属于每个实例化的类: 

template <typename T>
class Factory {
private:
    static int objectCount;
public:
    static T* create() {
        ++objectCount;
        return new T();
    }
    
    static int getObjectCount() {
        return objectCount;
    }
};

// 静态成员的初始化
template <typename T>
int Factory<T>::objectCount = 0;

// 使用示例
class MyClass {};

int main() {
    MyClass* obj1 = Factory<MyClass>::create();
    MyClass* obj2 = Factory<MyClass>::create();
    
    std::cout << "MyClass objects created: " << Factory<MyClass>::getObjectCount() << std::endl;
    
    delete obj1;
    delete obj2;
    
    return 0;
}

7.3 static 成员与模板特化

当模板被特化时,static 成员也会被特化: 

template <typename T>
class Logger {
public:
    static void log(const T& value) {
        std::cout << "Generic log: " << value << std::endl;
    }
};

// 模板特化
template <>
class Logger<int> {
public:
    static void log(const int& value) {
        std::cout << "Specialized int log: " << value << std::endl;
    }
};

int main() {
    Logger<double>::log(3.14);  // 调用通用版本
    Logger<int>::log(42);  // 调用特化版本
    return 0;
}

八、总结与最佳实践

8.1 类模板成员的关键特性

  • 类模板成员函数在使用时才会实例化,减少代码冗余
  • 非类型参数允许模板接受编译时常量,增强模板灵活性
  • 友元声明提供了控制访问权限的精细粒度
  • 成员模板允许在类模板内部定义更灵活的模板结构
  • 静态成员属于每个实例化的类,而非类模板本身

8.2 最佳实践建议

  • 分离接口与实现:使用包含模型(#include "impl.h")分离类模板的接口和实现,保持头文件简洁。

  • 谨慎使用友元:友元会破坏封装性,尽量减少使用。如果需要,优先使用特定实例化的友元。

  • 显式管理资源:在类模板中管理动态资源时(如内存、文件句柄),确保正确实现 RAII(资源获取即初始化)原则。

  • 静态成员初始化:确保每个静态数据成员在类模板外部初始化,避免链接错误。

  • 异常安全:在成员函数中实现强异常安全保证,特别是在涉及内存分配和资源释放的操作中。

类模板的强大之处不仅在于其类型抽象能力,更在于其丰富的成员机制能够满足各种复杂场景的需求。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

byte轻骑兵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值