【C++模板与泛型编程】类模板成员 、类模板成员函数1.1 类模板成员函数的定义与实例化类模板的成员函数本身也是模板需要在使用时根据上下文实例化。成员函数可以在类体内定义隐式内联也可以在类体外定义。示例类体内定义成员函数代码语言javascriptAI代码解释templatetypename T class Stack { private: std::vectorT 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; } };示例类体外定义成员函数代码语言javascriptAI代码解释templatetypename T class Stack { private: std::vectorT elements; public: void push(const T value); T pop(); }; // 类体外定义成员函数 templatetypename T void StackT::push(const T value) { elements.push_back(value); } templatetypename T T StackT::pop() { if (elements.empty()) throw std::out_of_range(Stack is empty); T top elements.back(); elements.pop_back(); return top; }1.2 成员函数的实例化规则类模板的成员函数只有在被调用或取地址时才会被实例化。这种延迟实例化机制可以避免不必要的代码生成。代码语言javascriptAI代码解释templatetypename T class Container { public: void printType() { std::cout typeid(T).name() std::endl; } void unusedFunction() { // 包含无效代码但未被调用时不会实例化 T::undefined_member; // 仅在调用时触发编译错误 } }; int main() { Containerint c; c.printType(); // 实例化printTypeint() // c.unusedFunction(); // 若取消注释将触发编译错误 return 0; }二、非类型形参的模板实参类模板不仅可以接受类型参数还可以接受非类型参数如整数、指针、引用等。非类型参数在编译时必须是常量表达式。2.1 非类型参数的基本语法代码语言javascriptAI代码解释templatetypename 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; } }; // 使用非类型参数实例化 Arrayint, 10 intArray; // 创建包含10个整数的数组 Arraydouble, 5 doubleArray; // 创建包含5个双精度浮点数的数组2.2 非类型参数的限制非类型参数必须满足以下条件整数类型包括枚举指针或引用类型成员指针类型字面类型C20 起代码语言javascriptAI代码解释// 错误示例非类型参数不能是浮点类型 // templatetypename T, double N class InvalidArray {}; // 编译错误 // 正确示例非类型参数可以是指针 templatetypename T, T* ptr class PointerWrapper { // ... }; int globalVar 42; PointerWrapperint, globalVar wrapper;三、类模板中的友元声明类模板中的友元声明允许外部类、函数或其他模板访问其私有成员。友元声明可以分为三类普通友元、模板友元特定实例化或所有实例化和成员模板友元。3.1 普通友元声明代码语言javascriptAI代码解释templatetypename T class Container { private: T value; public: Container(const T val) : value(val) {} // 普通友元函数 friend void printValue(const ContainerT obj) { std::cout Value: obj.value std::endl; } }; // 使用友元函数 int main() { Containerint c(42); printValue(c); // 输出: Value: 42 return 0; }3.2 模板友元声明代码语言javascriptAI代码解释templatetypename T class Container; // 前向声明 // 模板函数声明 templatetypename T void printContainer(const ContainerT obj); templatetypename T class Container { private: T value; public: Container(const T val) : value(val) {} // 特定实例化的模板友元 friend void printContainerT(const ContainerT obj); // 所有实例化的模板友元 templatetypename U friend void globalFriendFunction(const ContainerU obj); }; // 模板友元函数的定义 templatetypename T void printContainer(const ContainerT obj) { std::cout Template friend: obj.value std::endl; } templatetypename U void globalFriendFunction(const ContainerU obj) { std::cout Global friend: obj.value std::endl; }四、Queue 和 QueueItem 的友元声明示例队列Queue是一种常见的数据结构通常由节点QueueItem组成。通过友元声明Queue 可以访问 QueueItem 的私有成员。4.1 Queue 和 QueueItem 的实现代码语言javascriptAI代码解释// QueueItem类表示队列中的节点 templatetypename T class QueueItem; // Queue类表示队列 templatetypename T class Queue { private: QueueItemT* head; QueueItemT* 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 QueueItemT; }; // QueueItem类的实现 templatetypename T class QueueItem { private: T value; QueueItem* next; // 私有构造函数只能由Queue创建 QueueItem(const T val) : value(val), next(nullptr) {} // 私有析构函数只能由Queue销毁 ~QueueItem() default; // 声明Queue为友元类允许访问其私有成员 friend class QueueT; }; // Queue类成员函数的实现 templatetypename T QueueT::~Queue() { while (!empty()) { pop(); } } templatetypename T void QueueT::push(const T value) { QueueItemT* newItem new QueueItemT(value); if (empty()) { head tail newItem; } else { tail-next newItem; tail newItem; } } templatetypename T void QueueT::pop() { if (empty()) return; QueueItemT* oldHead head; head head-next; delete oldHead; if (head nullptr) { tail nullptr; } }五、成员模板类模板的成员本身也可以是模板称为成员模板。成员模板可以是成员函数模板或嵌套类模板。5.1 成员函数模板代码语言javascriptAI代码解释#include iostream templatetypename T class Container { private: T value; public: // 声明所有Container实例化类型为友元 templatetypename U friend class Container; // 友元声明 Container(const T val) : value(val) {} // 成员函数模板转换构造函数 templatetypename U Container(const ContainerU other) : value(other.value) { // 现在可以访问other.value std::cout Template conversion constructor std::endl; } T getValue() const { return value; } }; int main() { Containerdouble d(3.14); Containerint i(d); // 调用模板转换构造函数 std::cout i.getValue() std::endl; // 输出: 3 return 0; }5.2 嵌套类模板代码语言javascriptAI代码解释templatetypename T class Outer { private: T value; public: Outer(const T val) : value(val) {} // 嵌套类模板 templatetypename U class Inner { private: U innerValue; public: Inner(const U val) : innerValue(val) {} void printOuterValue(const OuterT outer) { std::cout Outer value: outer.value std::endl; } }; }; // 使用嵌套类模板 int main() { Outerint outer(42); Outerint::Innerdouble inner(3.14); inner.printOuterValue(outer); // 输出: Outer value: 42 return 0; }六、完整的 Queue 类实现6.1 完整的 Queue 类代码下面是一个完整的队列类实现包含了前面讨论的各种特性代码语言javascriptAI代码解释#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 QueueT q); // QueueItem类模板 template typename T class QueueItem { private: T data; QueueItem* next; // 构造函数设为私有只能由Queue创建 QueueItem(const T val) : data(val), next(nullptr) {} // 友元声明 friend class QueueT; friend std::ostream operator T(std::ostream os, const QueueT q); }; // Queue类模板 template typename T class Queue { private: QueueItemT* head; QueueItemT* 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 QueueU other); // 友元声明 friend std::ostream operator T(std::ostream os, const QueueT q); }; // 拷贝构造函数的实现 template typename T QueueT::Queue(const Queue other) : head(nullptr), tail(nullptr) { for (QueueItemT* p other.head; p ! nullptr; p p-next) { push(p-data); } } // 析构函数的实现 template typename T QueueT::~Queue() { while (!empty()) { pop(); } } // 赋值运算符的实现 template typename T QueueT QueueT::operator(const Queue other) { if (this ! other) { // 清空当前队列 while (!empty()) { pop(); } // 复制另一个队列的元素 for (QueueItemT* p other.head; p ! nullptr; p p-next) { push(p-data); } } return *this; } // 入队操作的实现 template typename T void QueueT::push(const T value) { QueueItemT* newItem new QueueItemT(value); if (empty()) { head tail newItem; } else { tail-next newItem; tail newItem; } } // 出队操作的实现 template typename T void QueueT::pop() { if (empty()) { throw std::underflow_error(Queue is empty); } QueueItemT* oldHead head; head head-next; if (head nullptr) { tail nullptr; } delete oldHead; } // 获取队首元素的实现 template typename T T QueueT::front() { if (empty()) { throw std::underflow_error(Queue is empty); } return head-data; } template typename T const T QueueT::front() const { if (empty()) { throw std::underflow_error(Queue is empty); } return head-data; } // 判断队列是否为空的实现 template typename T bool QueueT::empty() const { return head nullptr; } // 成员函数模板从另一种类型的队列复制 template typename T template typename U QueueT QueueT::operator(const QueueU other) { // 清空当前队列 while (!empty()) { pop(); } // 创建一个临时队列用于复制 QueueU temp other; // 从临时队列中取出元素并添加到当前队列 while (!temp.empty()) { push(temp.front()); // 通过公共接口访问元素 temp.pop(); // 通过公共接口访问元素 } return *this; } // 友元函数输出队列内容 template typename T std::ostream operator(std::ostream os, const QueueT q) { os Queue: ; for (QueueItemT* p q.head; p ! nullptr; p p-next) { os p-data ; } return os; } #endif // QUEUE_H6.2 使用示例下面是使用这个队列类的示例代码代码语言javascriptAI代码解释#include iostream #include queue.h int main() { try { // 创建一个整数队列 Queueint 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; // 创建一个字符串队列 Queuestd::string stringQueue; stringQueue.push(Hello); stringQueue.push(World); std::cout stringQueue std::endl; // 使用成员函数模板进行类型转换 Queuedouble doubleQueue; Queueint 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; }代码语言javascriptAI代码解释// ---------- Queue.h ---------- #ifndef QUEUE_H #define QUEUE_H #include iostream #include stdexcept // 前向声明 templatetypename T class Queue; templatetypename T class QueueItem; // QueueItem类表示队列中的节点 templatetypename T class QueueItem { private: T value; QueueItem* next; // 私有构造函数只能由Queue创建 explicit QueueItem(const T val) : value(val), next(nullptr) {} // 声明Queue为友元类允许访问其私有成员 friend class QueueT; // 禁用拷贝构造和赋值运算符 QueueItem(const QueueItem) delete; QueueItem operator(const QueueItem) delete; }; // Queue类表示队列 templatetypename T class Queue { private: QueueItemT* head; QueueItemT* 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 QueueT queue) { os Queue: ; QueueItemT* current queue.head; while (current ! nullptr) { os current-value ; current current-next; } return os; } }; // 初始化静态成员 templatetypename T size_t QueueT::instanceCount 0; // 拷贝构造函数的实现 templatetypename T QueueT::Queue(const Queue other) : head(nullptr), tail(nullptr) { instanceCount; std::cout Queue copied. Total instances: instanceCount std::endl; QueueItemT* current other.head; while (current ! nullptr) { push(current-value); current current-next; } } // 赋值运算符的实现 templatetypename T QueueT QueueT::operator(const Queue other) { if (this ! other) { clear(); QueueItemT* current other.head; while (current ! nullptr) { push(current-value); current current-next; } } return *this; } // push操作的实现 templatetypename T void QueueT::push(const T value) { QueueItemT* newItem new QueueItemT(value); if (empty()) { head tail newItem; } else { tail-next newItem; tail newItem; } } // pop操作的实现 templatetypename T void QueueT::pop() { if (empty()) { throw std::out_of_range(Queue is empty); } QueueItemT* oldHead head; head head-next; delete oldHead; if (head nullptr) { tail nullptr; } } // front操作的实现 templatetypename T T QueueT::front() { if (empty()) { throw std::out_of_range(Queue is empty); } return head-value; } templatetypename T const T QueueT::front() const { if (empty()) { throw std::out_of_range(Queue is empty); } return head-value; } // size操作的实现 templatetypename T size_t QueueT::size() const { size_t count 0; QueueItemT* current head; while (current ! nullptr) { count; current current-next; } return count; } // clear操作的实现 templatetypename T void QueueT::clear() { while (!empty()) { pop(); } } #endif // QUEUE_H七、类模板中的 static 成员7.1 static 成员的基本概念类模板中的 static 成员属于每个实例化的类而不是类模板本身。每个不同的实例化类都有自己独立的 static 成员代码语言javascriptAI代码解释template typename T class Counter { private: static int count; public: Counter() { count; } ~Counter() { --count; } static int getCount() { return count; } }; // 静态成员的初始化 template typename T int CounterT::count 0; int main() { Counterint c1, c2; std::cout Counterint count: Counterint::getCount() std::endl; // 输出2 Counterdouble d1; std::cout Counterdouble count: Counterdouble::getCount() std::endl; // 输出1 return 0; }7.2 static 成员函数类模板中的 static 成员函数也属于每个实例化的类代码语言javascriptAI代码解释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 FactoryT::objectCount 0; // 使用示例 class MyClass {}; int main() { MyClass* obj1 FactoryMyClass::create(); MyClass* obj2 FactoryMyClass::create(); std::cout MyClass objects created: FactoryMyClass::getObjectCount() std::endl; delete obj1; delete obj2; return 0; }7.3 static 成员与模板特化当模板被特化时static 成员也会被特化代码语言javascriptAI代码解释template typename T class Logger { public: static void log(const T value) { std::cout Generic log: value std::endl; } }; // 模板特化 template class Loggerint { public: static void log(const int value) { std::cout Specialized int log: value std::endl; } }; int main() { Loggerdouble::log(3.14); // 调用通用版本 Loggerint::log(42); // 调用特化版本 return 0; }八、总结与最佳实践8.1 类模板成员的关键特性类模板成员函数在使用时才会实例化减少代码冗余非类型参数允许模板接受编译时常量增强模板灵活性友元声明提供了控制访问权限的精细粒度成员模板允许在类模板内部定义更灵活的模板结构静态成员属于每个实例化的类而非类模板本身8.2 最佳实践建议分离接口与实现使用包含模型#include impl.h分离类模板的接口和实现保持头文件简洁。谨慎使用友元友元会破坏封装性尽量减少使用。如果需要优先使用特定实例化的友元。显式管理资源在类模板中管理动态资源时如内存、文件句柄确保正确实现 RAII资源获取即初始化原则。静态成员初始化确保每个静态数据成员在类模板外部初始化避免链接错误。异常安全在成员函数中实现强异常安全保证特别是在涉及内存分配和资源释放的操作中。