5、子类模板访问基类模板
在子类模板中访问那些在基类模板中声明依赖于模板参数的符号,应该在它前面加上作用域限定符“::”或者显式使用this指针,否则编译器将试图在全局域中寻找该符号,引发错误。
举例:inherit.cpp
#include<iostream>
#include<cstdio>
using namespace std;
class A{
public:
int m_var;
void foo(void)const{}
class X{};
};
class B:public A{
public:
void bar(void)const{
cout<</*A::*/m_var<<endl;
/*A::*/foo();
/*A::*/X x;
}
};
template<typename T>
class C{
public:
int m_var;
void foo(void)const{}
class X{};
void exit (int status)const{
cout<<"再见!"<<endl;
}
};
template<typename T>
class D:public C<T>{
public:
void bar(void)const{
// cout<<C<T>::m_var<<endl;
cout<<this->m_var<<endl;//和上一行等价
// C<T>::foo();
this->foo();
typename C<T>::X x;
// C<T>::exit(0);
this->exit(0);
}
};
int main(){
Bb;
b.bar();
D<int> d;
d.bar();
return 0;
}
tarena@tarena-virtual-machine:~/day49$./a.out
134514601
-1218035724
再见!
6、模板型模板参数
类模板的模板参数如果结合的实参不是具体类型而是另一个模板,那么不能使用typename关键字声明该参数,而要写明其所结合的类模板实参的原型:
typename<模板形参表>class 模板型模板参数名
对于函数模板,GUN编译器允许为其传递模板型模板参数,但是c++标准并不支持
举例:
#include<iostream>
using namespace std;
template<typename T>
class Array{
public:
void pushBack(T const& data){
cout<<"向数组尾端压入数据"<<endl;
}
void popBack(void){
cout<<"向数据尾端弹出数据"<<endl;
}
};
template<typename T>
class List{
public:
void pushBack(T const& data){
cout<<"向链表尾端压入数据"<<endl;
}
void popBack(void){
cout<<"向链表尾端弹出数据"<<endl;
}
};
template<typename T,template<typename>classC>
class Stack{
public:
void push(T const &data){
m_c.pushBack(data);
}
void pop(void){
m_c.popBack();
}
private:
C<T> m_c;
};
int main(){
Stack<int,Array> sia;
sia.push(100);
sia.pop();
Stack<int,List> sil;
sil.push(100);
sil.pop();
return 0;
}
tarena@tarena-virtual-machine:~/day49$./a.out
向数组尾端压入数据
向数据尾端弹出数据
向链表尾端压入数据
向链表尾端弹出数据
7、嵌套模板的外部定义
如果将嵌套于一个类模板的内部模板放到包装模板的外部定义,需要按照作用域层次的顺序从外到内,从前到后依次使用独立的template字句声明其模板参数表。
举例:out.cpp
#include <iostream>
using namespace std;
/*
template<typename A>
class X {
public:
template<typenameB>
classY {
public:
template<typenameC>
voidfoo (void) {}
};
};
*/
template<typename A>
class X {
public:
template<typenameB>
classY;
};
template<typename A>
template<typenameB>
class X<A>::Y {
public:
template<typenameC>
voidfoo (void);
};
template<typename A>
template<typenameB>
template<typenameC>
void X<A>::Y<B>::foo (void) {
}
int main (void) {
return0;
}
8、“零”初始化
基本类型不存在缺省构造函数,违背显式初始化的局部变量和成员变量具有一个未定义的初值。如果希望模板函数或者模板类中所有参数化类型的变量,无论是基本类型还是类类型,都能以缺省方式被初始化,就必须显式进行缺省构造,即“零”初始化。
T var =T () ; // 局部变量
….:m_var() …. // 成员变量
举例:init.cpp
#include <iostream>
using namespace std;
template<typename T>
void foo (void) {
Tvar = T ();
// intvar = int ();
// stringvar = string ();
// Integervar = Integer ();
cout<< '[' << var << ']' << endl;
}
template<typename T>
class Foo {
public:
Foo(void) : m_var () {}
Tm_var;
};
class Integer {
public:
Integer(int arg = 0) : m_var (arg) {}
friendostream& operator<< (ostream& os,
Integerconst& i) {
returnos << i.m_var;
}
private:
intm_var;
};
int main (void) {
/*
foo<int>();
foo<string>();
foo<Integer>();
*/
Foo<int>fi;
cout<< '[' << fi.m_var << ']'<< endl;
Foo<string>fs;
cout<< '[' << fs.m_var << ']' << endl;
Foo<Integer>fn;
cout<< '[' << fn.m_var <<']' << endl;
return0;
}
tarena@tarena-virtual-machine:~/day49/day03$./a.out
[0]
[]
[0]
9、虚函数和多态
1)类模板中可以声明虚函数,而且只要实例化该模板时所提供的类型实参不违背虚函数有效覆盖的条件,就可以形成多态。
2)由于模板函数的延迟编译要晚于类或者类模板中虚表的构建,因此模板函数不能同时又是虚函数。
举例:vf.cpp
#include <iostream>
using namespace std;
template<typename A, typename B>
class X {
public:
//模板中的虚函数
virtualA foo (B arg) const {
cout<< "X::foo" << endl;
returnA ();
}
//不可以声明虚模板函数
/*
template<typenameC>
virtualvoid bar (void) const {}
*/
};
template<typename A, typename B,
typenameC, typename D>
class Y : public X<C, D> {
public:
Afoo (B arg) const {
cout<< "Y::foo" << endl;
returnA ();
}
};
int main (void) {
Y<int,double, int, double> y1;
X<int,double>& x1 = y1;
x1.foo(1.2);
Y<int,double, int, char> y2;
X<int,char>& x2 = y2;
x2.foo('A');
/*
Y<int,double, char, double> y3;
X<char,double>& x3 = y3;
x3.foo(1.2); */
return0;
}
tarena@tarena-virtual-machine:~/day49/day03$./a.out
Y::foo
X::foo
列出不能被声明为虚函数的函数:全局函数、静态成员函数、构造函数、模板型成员函数
一、编译模型
1、 单一模型:将函数、类和模板的声明、实现和使用放在同一个编译单元中
优点:一目了然、构建方便
缺点:难以维护,难以复用
2、 分离模型:将函数、类和模板的声明、实现和使用放在不同的源代码文件中。
优点:易于维护,易于复用、便于协作开发
缺点:需要编写专门的构建脚本----Makefile
致命问题:编译模板实现代码时编译器看不到对模板的使用,因此生成的目标模块中没有关于该模板的二进制指令,最终导致链接失败。
举例:Mkdir div
3、 包含模型:在模板声明文件的尾部包含模板实现文件,保证所有使用该模板的代码在头文件扩展以后都实际包含了该模板的声明和实现,保证延迟编译的顺利进行,链接成功。
优点:解决了分离模型的致命问题-----链接失败
缺点:模板的实现代码必须公开、延长编译时间
举例:mkdir inc
4、 实例模型:在模板的实现中包含对该模板的显示实例化代码,旨在迫使编译器在看到对模板的使用之前,先做二次编译,保证链接通过。
优点:模板的实现代码不必公开,编译时间不受影响
缺点:实例化类型有限,无法实现绝对通用
举例:mkdir ins
5、 导出模型:通过export关键字将模板声明为导出,编译器会将该模板一次编译之后形成的内部表示缓存到其目标文件中(.o)中,在链接阶段结合使用该模板的代码所提供的类型实参,完成二次编译。
优点:代码分离,实现不公开、编译速度快、类型通用
缺点:绝大多数编译器并不支持该模板
举例:mkdir exp
二、容器、迭代器和泛型算法
容器:基于类模板语法实现泛型数据结构。
迭代器:基于指针语法实现面向不同容器的统一访问方式。
泛型算法:基于函数模板语法实现泛型常用算法。
双向线性链表容器
正向顺序可写迭代器
线性查找算法
vi.list.cpp
#include <iostream>
#include <cstring>
#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& list) :m_head (NULL),
m_tail (NULL) {
for (Node* node = list.m_head;
node;node = node->m_next)
push_back (node->m_data);
}
List& operator= (List const& list) {
if (&list != this) {
List temp = list; // 拷贝构造
swap (m_head,temp.m_head);
swap (m_tail,temp.m_tail);
}
return *this;
}
// 获取首元素
T& front (void) {
if (empty ())
throw underflow_error ("链表下溢!");
return m_head->m_data;
}
Tconst& 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;
}
Tconst& 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 = 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) {
while (! empty ())
pop_back ();
}
// 判空
bool empty (void) const {
return ! m_head && ! m_tail;
}
// 大小(元素个数)
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> li;
li.push_front (30);
li.push_front (20);
li.push_front (15);
cout << li << endl; // 15 20 30
li.front () -= 5;
cout << li << endl; // 10 20 30
List<int> const& cr = li;
// ++cr.front ();
cout << cr.front () << endl;
li.pop_front (); // 10
cout << li << endl; // 20 30
li.push_back (40);
li.push_back (50);
li.push_back (55);
cout << li << endl; // 20 30 40 50 55
li.back () += 5;
cout << li << endl; // 20 30 40 50 60
List<int> const* cp = &li;
// cp->back ()--;
cout << cp->back () << endl; // 60
li.pop_back ();
cout << li << endl; // 20 30 40 50
li.push_front (30);
li.push_front (30);
li.push_back (30);
li.push_back (30);
cout << li << endl; // 30 30 20 30 40 50 30 30
li.remove (30);
cout << li << endl; // 20 40 50
cout << boolalpha << li.empty () << ' '
<< li.size () << endl; // false 3
li.clear ();
cout << li.empty () << ' '
<< li.size () << endl; // true 0
}
void test2 (void) {
List<int> l1;
l1.push_back (100);
l1.push_back (200);
l1.push_back (300);
l1.push_back (400);
l1.push_back (500);
List <int> l2 = l1; // 拷贝构造
cout << "l1: " << l1 << endl;
cout << "l2: " << l2 << endl;
// 验证是否为深拷贝
l1.pop_front (); // 200 300 400 500
l2.pop_back (); // 100 200 300400
cout << "l1: " << l1 << endl;
cout << "l2: " << l2 << endl;
l2 = l1; // 拷贝赋值
cout << "l1: " << l1 << endl;
cout << "l2: " << l2 << endl;
l1.push_front (100);
l2.push_back (600);
cout << "l1: " << l1 << endl;
cout << "l2: " << l2 << endl;
}
// 进程入口
int main(void)
{
// test1 ();
test2 ();
return 0;
}
tarena@tarena-virtual-machine:~/day49/div$./a.out
l1: (100)(200)(300)(400)(500)
l2: (100)(200)(300)(400)(500)
l1: (200)(300)(400)(500)
l2: (100)(200)(300)(400)
l1: (200)(300)(400)(500)
l2: (200)(300)(400)(500)
l1: (100)(200)(300)(400)(500)
l2: (200)(300)(400)(500)(600)