四、模板特性 … 7.编译模型 … 导出模型:通过export关键字,将模板声明为导出,这样编译器在对模板进行一次编译时会将该模板内部表示,缓存在.o文件中,等到链接阶段,在结合实例化该模板的具体类型,补充做二次编译,得到相应的具体函数和具体类,最后完成二进制代码的链接。但是,很遗憾,目前阶段大部分C++编译器都不支持这种做法。因此,多数情况下依然采用包含模型或者预实例化模型,解决模板的分离编译问题。 代码:exp/ cmp.h
// 声明 export template<typename T> T const& min(T const& x, T const& y); export template<typename T> T const& max(T const& x, T const& y); export template<typename T> class Comparator { public: Comparator(T const& x, T const& y); T const& min(void) const; T const& max(void) const; private: T const& m_x; T const& m_y; };cmp.cpp
// 实现 #include "cmp.h" template<typename T> T const& min(T const& x, T const& y) { return x < y ? x : y; } template<typename T> T const& max(T const& x, T const& y) { return x < y ? y : x; } template<typename T> Comparator<T>::Comparator(T const& x, T const& y) : m_x(x), m_y(y) {} template<typename T> T const& Comparator<T>::min(void) const { return m_x < m_y ? m_x : m_y; } template<typename T> T const& Comparator<T>::max(void) const { return m_x < m_y ? m_y : m_x; }use.cpp
// 使用 #include <iostream> using namespace std; #include "cmp.h" int main(void) { int a = 123, b = 456; double c = 1.23, d = 4.56; string e = "world", f = "hello"; cout << ::min(a, b) << ' ' << ::max(a, b) << endl; cout << ::min(c, d) << ' ' << ::max(c, d) << endl; cout << ::min(e, f) << ' ' << ::max(e, f) << endl; Comparator<int> ci(a, b); cout << ci.min() << ' ' << ci.max() << endl; Comparator<double> cd(c, d); cout << cd.min() << ' ' << cd.max() << endl; Comparator<string> cs(e, f); cout << cs.min() << ' ' << cs.max() << endl; return 0; }8.预编译头文件 提升编译速度的技巧。 将一些公用头文件,或者是包含大量与模板实现有关的代码,放到一个独立的.h文件中,将该.h文件单独编译为一个.gch文件,即预编译头文件。编译任何包含此独立头文件的cpp文件时,都不再编译头文件,而是直接从.gch文件中读取内容,以此减少对相同代码重复编译的次数,提升编译速度,缩短编译时间。 代码:hello.h、hello.cpp 注意,一旦修改了与预编译头文件有关的文件内容,一定要重新编译生成预编译头文件,以反映所作出的改变。 五、容器、迭代器和泛型算法 容器:双向线性链表容器 迭代器:正向可写迭代器 泛型算法:线性查找算法 代码:list.cpp
#include <cstring> #include <iostream> #include <stdexcept> using namespace std; // 双向线性链表容器 template<typename T> class List { public: // 构造器、析构器、拷贝构造器、 // 拷贝赋值运算符 List(void) : m_head(NULL), m_tail(NULL) {} ~List(void) { clear(); } List(List const& that) : m_head(NULL), m_tail(NULL) { for (Node* node = that.m_head; node; node = node->m_next) push_back(node->m_data); } List& operator=(List const& that) { if (&that != this) { List list(that); swap(m_head, list.m_head); swap(m_tail, list.m_tail); } return *this; } // 获取首元素 T& front(void) { if (empty()) throw underflow_error( "链表下溢!"); return m_head->m_data; } T const& front(void) const { return const_cast<List&>( *this).front(); } // 向首部压入 void push_front(T const& data) { m_head = new Node(data, NULL, m_head); if (m_head->m_next) m_head->m_next->m_prev = m_head; else m_tail = m_head; } // 从首部弹出 void pop_front(void) { if (empty()) throw underflow_error( "链表下溢!"); Node* next = m_head->m_next; delete m_head; m_head = next; if (m_head) m_head->m_prev = NULL; else m_tail = NULL; } // 获取尾元素 T& back(void) { if (empty()) throw underflow_error( "链表下溢!"); return m_tail->m_data; } T const& back(void) const { return const_cast<List&>( *this).back(); } // 向尾部压入 void push_back(T const& data) { m_tail = new Node(data, m_tail); if (m_tail->m_prev) m_tail->m_prev->m_next = m_tail; else m_head = m_tail; } // 从尾部弹出 void pop_back(void) { if (empty()) throw underflow_error( "链表下溢!"); Node* prev = m_tail->m_prev; delete m_tail; m_tail = prev; if (m_tail) m_tail->m_next = NULL; else m_head = NULL; } // 删除所有匹配元素 void remove(T const& data) { for (Node* node = m_head, *next; node; node = next) { next = node->m_next; if (node->m_data == data) { if (node->m_prev) node->m_prev->m_next = node->m_next; else m_head = node->m_next; if (node->m_next) node->m_next->m_prev = node->m_prev; else m_tail = node->m_prev; delete node; } } } // 清空 void clear(void) { for (Node* next; m_head; m_head = next) { next = m_head->m_next; delete m_head; } m_tail = NULL; } // 判空 bool empty(void) const { return m_head == NULL && m_tail == NULL; } // 大小 size_t size(void) const { size_t nodes = 0; for (Node* node = m_head; node; node = node->m_next) ++nodes; return nodes; } // 插入流 friend ostream& operator<<(ostream& os, List const& list) { for (Node* node = list.m_head; node; node = node->m_next) os << *node; return os; } private: // 节点 class Node { public: // 构造器 Node(T const& data, Node* prev = NULL, Node* next = NULL) : m_data(data), m_prev(prev), m_next(next) {} // 插入流 friend ostream& operator<<( ostream& os, Node const& node) { return os << '(' << node.m_data << ')'; } T m_data; // 数据 Node* m_prev; // 前指针 Node* m_next; // 后指针 }; Node* m_head; // 头指针 Node* m_tail; // 尾指针 }; // 测试用例 void test1 (void) { List<int> list; cout << list << endl; // 空 list.push_front(30); list.push_front(20); list.push_front(10); cout << list << endl; // 10 20 30 --list.front(); cout << list << endl; // 9 20 30 List<int> const& cr = list; cout << cr.front() << endl; // 9 //cr.front()++; list.pop_front(); cout << list << endl; // 20 30 list.push_back(40); list.push_back(50); list.push_back(60); cout << list << endl; // 20 30 40 50 60 list.back()++; cout << list << endl; // 20 30 40 50 61 List<int> const* cp = &list; cout << cp->back() << endl; // 61 //--cp->back(); list.pop_back(); cout << list << endl; // 20 30 40 50 } void test2(void) { List<int> l1; l1.push_back(10); l1.push_back(20); l1.push_back(30); List<int> l2 = l1; // 拷贝构造 cout << "l1: " << l1 << endl; cout << "l2: " << l2 << endl; ++l1.front(); ++l2.back(); cout << "l1: " << l1 << endl; cout << "l2: " << l2 << endl; l1 = l2; // 拷贝赋值 cout << "l1: " << l1 << endl; cout << "l2: " << l2 << endl; ++l1.front(); ++l2.back(); cout << "l1: " << l1 << endl; cout << "l2: " << l2 << endl; } void test3(void) { int a[] = {10, 20, 30, 20, 40, 20, 50, 20, 20, 60}; List<int> list; for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i) list.push_back(a[i]); cout << list << endl; list.remove(20); cout << list << endl; } int main(void) { //test1(); //test2(); test3(); return 0; }六、标准模板库(STL) 1.主要内容 1)容器 A.线性容器:向量(vector)、双端队列(deque)、列表(list) B.适配器容器:堆栈(stack)、队列(queue)、优先队列(priority_queue) C.关联容器:映射(map)、多重映射(multimap)、集合(set)、多重集合(multiset) 2)迭代器: A.成员迭代器:正向和反向、可写和只读、随机和顺序 B.全局迭代器:插入迭代器、IO流迭代器 3)泛型算法 查找、排序、拆分、合并、交换、遍历,等等 2.向量(vector) 1)基本特性和实例化 向量中的元素被存储在一段连续的内存空间中,通过下标随机访问向量中的元素,其效率等价于数组。 向量的内存空间会随着新元素的加入而自动增长。 内存空间的连续性不会妨碍向量元素的持续增加。 在创建向量的时候,也可以提前预分配内存空间,只要这些空间够用就不再分配新的内存空间,除非使用用发现不够用了,再自动扩充内存。 #include vector<元素类型> 向量对象; vector vi; 空向量,占内存。 vector<元素类型> 向量对象(初始大小); vector vi(5); // 0 0 0 0 0 vector<元素类型> 向量对象(初始大小, 初值); vector vi(5, 10); // 10 10 10 10 10 vector<元素类型> 向量对象(起始迭代器, 终止迭代器); 凡是用起始迭代器和终止迭代器表示容器中的元素范围,终止迭代器时范围中最后一个元素的下一个位置的迭代器: [起始迭代器, 终止迭代器) 1 2 3 4 5 6 7 ^ ^ | | 起始 终止 -> 2 3 4 5 代码:vec1.cpp
#include <iostream> #include <vector> using namespace std; void print(vector<int> const& vec) { cout << "容量:" << vec.capacity() << endl; cout << "大小:" << vec.size() << endl; cout << "字节:" << sizeof(vec) << endl; cout << "元素:"; for (size_t i = 0; i < vec.size(); ++i) cout << vec[i] << ' '; cout << endl; } int main(void) { vector<int> v1; print(v1); vector<int> v2(5); print(v2); vector<int> v3(5, 10); print(v3); v3.push_back(20); print(v3); int a[] = {1, 2, 3, 4, 5, 6, 7}; vector<int> v4(&a[2], &a[5]); print(v4); vector<int> v5(&a[0], &a[7]); print(v5); return 0; }2)迭代器 连续内存的容器->随机迭代器->处理顺序迭代器提供的功能以外还支持与整数的加减法运算,以及同类型迭代器之间的大小比较和减法运算。 iterator: 正向可写迭代器 const_iterator: 正向只读迭代器 reverse_iterator: 反向可写迭代器 const_reverse_iterator: 反向只读迭代器