本章内容包括:
- has-a关系
- 包含对象成员的类
- 模板类和valarray
- 私有和保护继承
- 多重继承
- 虚基类
- 创建类模板
- 使用类模板
- 模板具体化
14.1 包含对象成员的类
14.1.1 valarray类简介(数值数组模板类)
valarray<int> q_values;? ? ? ? ? ? ?//int数组
valarray<double> weights;? ? ? ? //double数组
回顾:
vector<double> vd(n);
array<int, 5> ai;
使用构造函数的例子:
double gpa[5] = { 3.1, 3.5, 3.8, 2.9, 3.3 };
valarray<double> v1;? ? ? ? ? ? ? ? ? ? ? ? //长度为0的空数组
valarray<int> v2(8);? ? ? ? ? ? ? ? ? ? ? ? ? //指定长度的空数组
valarray<int> v3(10, 8);? ? ? ? ? ? ? ? ? ? //所有元素(8)被初始化为指定值(10)的数组
valarray<double> v4(gpa, 4);? ? ? ? ? ?//用常规数组(gpa)的值(前4个)进行初始化
valarray<int> v5 = { 20, 32, 17, 9 };? //初始化列表
?valarray类方法:
- operator[]()
- size()
- sum()
- max()
- min()
14.1.2 Student类设计
is-a模型:水果->香蕉
has-a模型:午餐->香蕉
student类的成员函数可以通过name和score使用string类和valarray类的公有方法,来操作string类的对象name和valarray类的对象score,但并不是基类和派生的继承关系。
一个类对象通过另一个类对象调用另一个类的公有方法
14.1.3 Student类示例
//程序清单 14.1 studentc.h
#pragma once
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
class Student
{
private:
//typedef valarray<double> ArrayDb;
using ArrayDb = valarray<double>; //放私有意味着只可以在student类中使用ArrDb
string name;
ArrayDb scores;
ostream& arr_out(ostream& os) const;
public:
Student() : name("Null Student"), scores() {}
// 长度为0的double空数组
//转换函数
explicit Student(const string& s) : name(s), scores() {}
//string类型显式转换为student类类型,禁止隐式转换
explicit Student(int n) : name("Nully"), scores(n) {}
//int类型显式转换为student类类型,包含长度为n的double空数组
Student(const string& s, int n) : name(s), scores(n) {}
//string和int类型的转换为student类类型
Student(const string& s, const ArrayDb& a) : name(s), scores(a) {}
Student(const string& s, const double* pd, int n) : name(s), scores(pd, n) {}
~Student(){}
double Average() const;
const string& Name() const { return name; } //显示名称
double& operator[](int i) { return scores[i]; } //修改某一门成绩
double operator[](int i) const { return scores[i]; } //只读赋值
friend istream& operator>>(istream& is, Student& stu);
friend istream& getline(istream& is, Student& stu);
friend ostream& operator<<(ostream& os, const Student& stu);
};
#endif // !STUDENTC_H_
?对于继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数:
hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) {...}
对于成员对象,构造函数则使用成员名:
Student(const char * str, const double * pd, int n) : name(str), score(pd, n) {}
//程序清单 14.2
#include "studentc.h"
double Student::Average() const
{
if (scores.size() > 0) //元素个数
return scores.sum() / scores.size();
else
return 0;
}
istream& operator>>(istream& is, Student& stu)
{
is >> stu.name;
return is;
}
istream& getline(istream& is, Student& stu)
{
getline(is, stu.name);
return is;
}
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << stu.name << ":\n";
stu.arr_out(os);
return os;
}
ostream& Student::arr_out(ostream& os) const
{
int i;
int lim = scores.size();
if (lim > 0)
{
for (i = 0; i < lim; i++)
{
os << scores[i] << " ";
if (i % 5 == 4) //每行5个
os << endl;
}
if (i % 5 != 0)
os << endl; //显示光标换行
}
else
os << "Empty array!";
return os;
}
//程序清单 14.3
#include <iostream>
#include "studentc.h"
const int pupils = 3; //3个学士
const int quizzes = 5; //5门成绩
void set(Student& sa, int n);
int main(void)
{
Student ada[pupils] = { Student(quizzes), Student(quizzes), Student(quizzes) };
int i;
for (i = 0; i < pupils; i++)
set(ada[i], quizzes);
cout << "\nStudent List:\n";
for (i = 0; i < pupils; i++)
cout << ada[i].Name() << endl;
cout << "\nResult:\n";
for ( i = 0; i < pupils; i++)
{
cout << ada[i];
cout << "average: " << ada[i].Average() << endl << endl;
}
return 0;
}
void set(Student& sa, int n)
{
cout << "Enter the student's name: ";
//调用friend istream& getline(istream& is, Student& stu)
getline(cin, sa);
cout << "Enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
//调用double& operator[](int n);
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
14.2 私有继承
- 包含将对象作为一个命名的成员对象添加到类中
- 私有继承将对象作为一个未被命名的继承对象添加到类中
14.2.1 Student类新版本
- ?包含版本提供两个被显式命名的对象成员
- 私有继承提供两个无名称的隐式对象成员
//程序清单 14.4
#pragma once
#ifndef STUDENTI_H_
#define STUDENTI_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
class Student : private string, private valarray<double>
//私有继承使用关键字private,多个基类的继承称为多重继承
{
private:
typedef valarray<double> ArrayDb;
//using ArrayDb = valarray<double>;
ostream& arr_out(ostream& os) const;
public:
//基类类名代替成员名
Student() : string("Null Student"), ArrayDb() {}
//转换函数
explicit Student(const string& s) : string(s), ArrayDb() {}
//string类型显式转换为student类类型,禁止隐式转换
explicit Student(int n) : string("Nully"), ArrayDb(n) {}
//int类型显式转换为student类类型,包含长度为n的double空数组
Student(const string& s, int n) : string(s), ArrayDb(n) {}
//string和int类型的转换为student类类型
Student(const string& s, const ArrayDb& a) : string(s), ArrayDb(a) {}
Student(const string& s, const double* pd, int n) : string(s), ArrayDb(pd, n) {}
~Student() {}
double Average() const;
const string& Name() const { return (const string&) *this; }
//显示名称
double& operator[](int i) { return ArrayDb::operator[](i); }
//修改某一门成绩
double operator[](int i) const { return ArrayDb::operator[](i); }
//只读赋值
friend istream& operator>>(istream& is, Student& stu);
friend istream& getline(istream& is, Student& stu);
friend ostream& operator<<(ostream& os, const Student& stu);
using valarray<double>::max;
};
#endif // !STUDENTI_H_
1.类名代替成员名?
//包含
Student(const string& s, const double* pd, int n) : name(s), scores(pd, n) {}
//私有
Student(const string& s, const double* pd, int n) : string(s), ArrayDb(pd, n) {}
2.访问基类方法:类名+::
//包含
double Student::Average() const
{
if (scores.size() > 0) //元素个数
return scores.sum() / scores.size();
else
return 0;
}
//私有
double Student::Average() const
{
if (ArrDb::size() > 0) //元素个数
return ArrDb::sum() / scores.size();
else
return 0;
}
3.访问基类对象:强制类型转换
//包含
const string& Name() const { return name; }
//私有
const string& Name() const { return (const string &) *this; }
4.访问基类友元:强制类型转换
//包含
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << stu.name << ":\n";
stu.arr_out(os);
return os;
}
//私有
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << (const string &) stu << ":\n";
stu.arr_out(os);
return os;
}
?派生类成员函数和友元通过通过强制类型转换访问基类成员,仅限某类型单个对象,多个不好使
//程序清单 14.5
#include "studenti.h"
double Student::Average() const
{
if (ArrayDb::size() > 0)
return ArrayDb::sum() / ArrayDb::size();
else
return 0;
}
istream& operator>>(istream& is, Student& stu)
{
is >> (string&) stu;
return is;
}
istream& getline(istream& is, Student& stu)
{
getline(is, (string&) stu);
return is;
}
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << (const string&) stu << ":\n";
stu.arr_out(os);
return os;
}
ostream& Student::arr_out(ostream& os) const
{
int i;
int lim = ArrayDb::size();
if (lim > 0)
{
for (i = 0; i < lim; i++)
{
os << ArrayDb::operator[](i) << " ";
if (i % 5 == 4) //每行5个
os << endl;
}
if (i % 5 != 0)
os << endl; //显示光标换行
}
else
os << "Empty array!";
return os;
}
//程序清单 14.6
#include <iostream>
#include "studenti.h"
const int pupils = 3; //3个学士
const int quizzes = 5; //5门成绩
void set(Student& sa, int n);
int main(void)
{
Student ada[pupils] = { Student(quizzes), Student(quizzes), Student(quizzes) };
int i;
for (i = 0; i < pupils; i++)
set(ada[i], quizzes);
cout << "\nStudent List:\n";
for (i = 0; i < pupils; i++)
cout << ada[i].Name() << endl;
cout << "\nResult:\n";
for (i = 0; i < pupils; i++)
{
cout << ada[i];
cout << "average: " << ada[i].Average() << endl;
//要让基类方法在派生类外可用,使用using声明,函数下放
cout << "max: " << ada[i].max() << endl << endl;
}
return 0;
}
void set(Student& sa, int n)
{
cout << "Enter the student's name: ";
//调用friend istream& getline(istream& is, Student& stu)
getline(cin, sa);
cout << "Enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
14.2.2 使用包含还是私有继承
- 多数C++倾向包含,首先易于理解
- 如果某个类需要3个string对象,可以使用包含声明3个独立的string成员,但私有继承只能使用一个
- 派生类可以重新定义虚函数,包含类不能
- 通常,应使用包含建立has-a关系;如果新类需要访问原有类的保护成员,或需要重新定义虚函数,则使用私有继承。
14.2.3 保护继承
- 保护继承是私有继承的变体,使用关键字protect
- 使用保护继承,基类的公有成员和保护成员都将成员派生类的保护成员
- 当从派生类再派生出另一个类时,私有继承和保护继承的区别便体现出来:
- 使用私有继承时,第三代类不能使用基类接口,因为基类方法在派生类中变成私有方法;使用保护继承时,基类公有方法在第一代中变成保护,第三代派生可以使用。
14.2.4 使用using重新定义访问权限
使用保护派生或私有派生时,基类的公有成员将成为派生类的保护成员或私有成员
要让基类方法在派生类外可用:
????????方法一:定义一个使用该基类方法的派生类方法
double Student::sum() const
{
return std::valarry<double>::sum();
}
? ? ? ? 方法二:使用using声明,函数下放。using声明只使用成员名——没有圆括号、函数特征标和返回类型
class Student : private std::string, private std::vaalarray<double>
{
...
public:
using std::valarray<double>::min;
using std::valarray<double>::max;
...
}
//就像Student的公有方法:
cout << "high score: " << ada[i].max() << endl;
14.3 多重继承
两个主要问题:
- 从两个不同的基类继承同名方法;
- 从两个或多个相关基类继承同一个类的多个实例
//程序清单 14.7 Worker0.h
#pragma once
#ifndef WORKER0_H_
#define WORKER0_H_
#include <iostream>
#include <string>
using namespace std;
class Worker
{
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const string& s, long n) : fullname(s), id(n) {}
virtual ~Worker() = 0; //纯虚函数,要在cpp中定义
virtual void Set();
virtual void Show() const;
private:
string fullname;
long id;
};
class Waiter : public Worker //不写public默认私有继承
{
public:
Waiter() : Worker(), panache(0) {}
Waiter(const string& s, long n, int p) : Worker(s, n), panache(p) {}
Waiter(const Worker& wk, int p) : Worker(wk), panache(p) {}
virtual ~Waiter() {}
virtual void Set();
virtual void Show() const;
private:
int panache;
};
class Singer : public Worker
{
protected:
enum { other, alto, contralto, soprano, bass, baritone, tenor };
const static int Vtypes = 7;
private:
static char* pv[Vtypes]; //静态字符串数组指针,声明中声明,方法中初始化
//不属于类,类似全局,所有对象共享一个
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const string& s, long n, int v = other) : Worker(s, n), voice(v) {}
Singer(const Worker& wk, int v = other) : Worker(wk), voice(v) {}
virtual ~Singer() {}
virtual void Set();
virtual void Show() const;
};
#endif // !WORKER0_H_
//程序清单 14.8
#include "worker0.h"
Worker::~Worker() {}
void Worker::Set()
{
cout << "Enter worker's name: ";
getline(cin, fullname);
cout << "Enter worker's ID: ";
cin >> id;
while (cin.get() != '\n') //数字字符串混合输入,消耗换行
continue;
}
void Worker::Show() const
{
cout << "Name: " << fullname << endl;
cout << "Employee ID: " << id << endl;
}
void Waiter::Set()
{
Worker::Set(); //类名+::派生类调用基类方法
cout << "Enter waiter's panache rating: ";
cin >> panache;
while (cin.get() != '\n')
continue;
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Show();
cout << "Panache rating: " << panache << endl;
}
//初始化时,使用::指出静态成员所属的类
char* Singer::pv[] = { (char*)"other", (char*)"alto", (char*)"contralto",
(char*)"soprano", (char*)"bass", (char*)"baritone", (char*)"tenor" };
//char* Singer::pv[] = { "other", "alto", "contralto",
// "soprano", "bass", "baritone", "tenor" };
//项目>>属性>>C/C++>>语言>>符合模式>>改成否
void Singer::Set()
{
Worker::Set();
cout << "Enter number for singer's vocal range:\n";
int i;
for ( i = 0; i < Vtypes; i++)
{
cout << i << ": " << pv[i] << " ";
if (3 == i % 4) //一行4个
cout << endl;
}
if (0 != i % 4) //最后光标换行
cout << endl;
//程序清单 14.9
#include <iostream>
#include "worker0.h"
const int LIM = 4;
int main(void)
{
Waiter bob("Bob Apple", 314L, 5);
Singer bev("Beverly Hills", 522L, 3);
Waiter w_temp;
Singer s_temp;
Worker* pw[LIM] = { &bob, &bev, &w_temp, &s_temp };
//基类指针(引用)可以指向基类对象/派生类对象
int i;
for ( i = 2; i < LIM; i++)
pw[i]->Set(); //看指针指向类型决定调用哪个set()
for ( i = 0; i < LIM; i++)
{
pw[i]->Show();
cout << endl;
}
return 0;
}
14.3.1 有多少worker
从Singer和Waiter公有派生出SingingWaiter:
class SingingWaiter : public Singer, public Waiter { ... }?
?SingingWaiter包含两个Worker组件
SingingWaiter ed;
Worker * pw = &ed;? ? ? ? //二义性
要改为:
Worker * pw1 = (Waiter *) &ed;
?Worker * pw2?= (Singer?*) &ed;
?应该只包含一个基类组件——虚基类
1. 虚基类(与虚函数无关)
虚基类使多个类(基类相同)派生的对象只继承一个基类对象
class Singer : virtual public Worker { ... };
class Waiter : public virtual Worker { ... };
2. 新的构造函数规则:
????????传统:层层传递
?
?
14.3.2 哪个方法:模块化
//程序清单 14.10
#pragma once
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <iostream>
#include <string>
using namespace std;
class Worker
{
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const string& s, long n) : fullname(s), id(n) {}
virtual ~Worker() = 0; //纯虚函数,要在cpp中定义
virtual void Set() = 0;
virtual void Show() const = 0;
private:
string fullname;
long id;
protected:
virtual void Data() const;
virtual void Get();
};
class Waiter : virtual public Worker //不写public默认私有继承
{
public:
Waiter() : Worker(), panache(0) {}
Waiter(const string& s, long n, int p) : Worker(s, n), panache(p) {}
Waiter(const Worker& wk, int p) : Worker(wk), panache(p) {}
virtual ~Waiter() {}
virtual void Set();
virtual void Show() const;
private:
int panache;
protected:
virtual void Data() const;
virtual void Get();
};
class Singer : public virtual Worker
{
protected:
enum { other, alto, contralto, soprano, bass, baritone, tenor };
const static int Vtypes = 7;
virtual void Data() const;
virtual void Get();
private:
static char* pv[Vtypes]; //静态字符串数组指针,声明中声明,方法中初始化
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const string& s, long n, int v = other) : Worker(s, n), voice(v) {}
Singer(const Worker& wk, int v = other) : Worker(wk), voice(v) {}
virtual ~Singer() {}
virtual void Set();
virtual void Show() const;
};
class SingingWaiter : public Waiter, public Singer
{
public:
SingingWaiter() {}
SingingWaiter(const string& s, long n, int p = 0, int v = other)
: Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
SingingWaiter(const Worker& wk, int p = 0, int v = other)
: Worker(wk), Waiter(wk, p), Singer(wk, v) {}
SingingWaiter(const Waiter& wt, int v = other)
: Worker(wt), Waiter(wt), Singer(wt, v) {}
SingingWaiter(const Singer& sg, int p = 0)
:Worker(sg), Waiter(sg, p), Singer(sg) {}
~SingingWaiter() {}
void Set();
void Show() const;
protected:
virtual void Data() const;
virtual void Get();
};
#endif // !WORKER0_H_
//程序清单 14.11
#include "workermi.h"
Worker::~Worker() {}
void Worker::Get()
{
getline(cin, fullname);
cout << "Enter worker's ID: ";
cin >> id;
while (cin.get() != '\n') //数字字符串混合输入,消耗换行
continue;
}
void Worker::Data() const
{
cout << "Name: " << fullname << endl;
cout << "Employee ID: " << id << endl;
}
void Waiter::Get()
{
cout << "Enter waiter's panache rating: ";
cin >> panache;
while (cin.get() != '\n')
continue;
}
void Waiter::Data() const
{
cout << "Panache rating: " << panache << endl;
}
void Waiter::Set()
{
cout << "Enter waiter's name: ";
Worker::Get();
Get();
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Data();
Data();
}
//初始化时,使用::指出静态成员所属的类
char* Singer::pv[] = { (char*)"other", (char*)"alto", (char*)"contralto",
(char*)"soprano", (char*)"bass", (char*)"baritone", (char*)"tenor" };
//char* Singer::pv[] = { "other", "alto", "contralto",
// "soprano", "bass", "baritone", "tenor" };
//项目>>属性>>C/C++>>语言>>符合模式>>改成否
void Singer::Get()
{
cout << "Enter number for singer's vocal range:\n";
int i;
for (i = 0; i < Vtypes; i++)
{
cout << i << ": " << pv[i] << " ";
if (3 == i % 4) //一行4个
cout << endl;
}
if (0 != i % 4) //最后光标换行
cout << endl;
while (cin >> voice && (voice < 0 || voice >= Vtypes))
cout << "Please enter a value >= 0 and <= " << Vtypes << endl;
while (cin.get() != '\n')
continue;
}
void Singer::Data() const
{
cout << "Vocal range: " << pv[voice] << endl;
}
void Singer::Set()
{
cout << "Enter singer's name: ";
Worker::Get();
Get();
}
void Singer::Show() const
{
cout << "Category: singer\n";
Worker::Data();
Data();
}
void SingingWaiter::Data() const
{
Waiter::Data();
Singer::Data();
}
void SingingWaiter::Get()
{
Waiter::Get();
Singer::Get();
}
void SingingWaiter::Set()
{
cout << "Enter singingwaiter's name: ";
Worker::Get();
Get();
}
void SingingWaiter::Show() const
{
cout << "Category: singing waiter\n";
Worker::Data();
Data();
}
//程序清单 14.12
#include <iostream>
#include "workermi.h"
#include <cstring>
const int SIZE = 3;
int main(void)
{
Worker* lolas[SIZE];
int ct;
for ( ct = 0; ct < SIZE; ct++)
{
char choice;
cout << "Enter the employee categlory:\n"
<< "w: waiter s: singer t: singing waiter q: quit\n";
cin >> choice;
while (strchr("wstq", choice) == NULL)
{
cout << "Enter w, s, t or q: ";
cin >> choice;
}
if (choice == 'q' || choice == 'Q')
break;
switch (choice)
{
case 'w':
lolas[ct] = new Waiter; break;
case 's':
lolas[ct] = new Singer; break;
case 't':
lolas[ct] = new SingingWaiter; break;
default:
break;
}
cin.get();
lolas[ct]->Set();
}
cout << "\nHere is your staff:\n";
int i;
for ( i = 0; i < ct; i++)
{
cout << endl;
lolas[i]->Show();
}
for ( i = 0; i < ct; i++)
delete lolas[i];
cout << "Bye.\n";
return 0;
}
14.4 类模板
模板提供参数化类型,即能将类型名作为参数传递给接收方来建立类或函数
14.4.1 定义类模板
//程序清单 14.13
#pragma once
#ifndef STACKTP_H_
#define STACKTP_H_
#include <iostream>
using namespace std;
//模板类
template <class Type>
//template <typename Type>
class Stack
{
public:
Stack();
bool isempty(void) const;
bool isfull(void) const;
bool push(const Type& item);
bool pop(Type& item);
private:
enum { MAX = 10 };
Type items[MAX];
int top;
};
//每个函数头都以相同的模板声明打头
template <class Type>
//将类限定符改为Stack<Type>::
Stack<Type>::Stack(void)
{
top = 0;
}
template <class Type>
bool Stack<Type>::isempty(void) const
{
return top == 0;
}
template <class Type>
bool Stack<Type>::isfull(void) const
{
return top == MAX;
}
template <class Type>
bool Stack<Type>::push(const Type& item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::pop(Type& item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
#endif // !STACKTP_H_
- ?模板不是类和成员函数定义,只是编译器指令
- 模板的具体实现,称为实例化和具体化
- 不能将模板成员函数放在独立的实现文件中
- 将所有模板信息放在一个头文件中
14.4.2 使用模板类
//程序清单 14.14
#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"
int main(void)
{
Stack<string> st;
char ch;
string po;
cout << "Enter A to add a purchase, P to process a PO, or Q to quit.\n";
while (cin >> ch && toupper(ch) != 'Q')
{
while (cin.get() != '\n')
continue;
if (!isalpha(ch))
{
cout << '\a';
continue;
}
switch (ch)
{
case 'A':
case 'a':
cout << "Enter a PO number add: ";
cin >> po;
if (st.isfull())
cout << "stack already full.\n";
else
st.push(po);
break;
case 'P':
case 'p':
if (st.isempty())
cout << "stack already empty.\n";
else
{
st.pop(po);
cout << "PO #" << po << " popped.\n";
}
break;
default:
break;
}
cout << "Enter A to add a purchase, P to process a PO, or Q to quit.\n";
}
cout << "Bye.\n";
return 0;
}
下面代码创建两个栈:一个存储int,一个存储string对象
Stack<int> kernels;
Stack<string> colonels;
?泛型标识符——Type——称为类型参数,类似变量,赋给的不能是数字,只能是类型
14.4.3 深入探讨模板类
可以用char指针替换string对象吗?可以,需要对程序做重大修改
1.不正确使用指针栈:
Stack<char *> st;
版本1:
string po 替换为:char * po;
仅仅创建指针,没有创建保存字符串的空间
版本2:
string po 替换为:char po[40];
数组完全与pop()方法冲突
版本3:
string po 替换为:char * po = new char [40];
栈没有保存每一个新字符
?2.正确使用指针栈:
让调用程序提供一个指针数组,其中每个指针都指向不同的字符串
//程序清单 14.15
#pragma once
#ifndef STCKTP1_H_
#define STCKTP1_H_
#include <iostream>
using namespace std;
template <class Type>
class Stack
{
public:
explicit Stack(int ss = SIZE); //显式转换函数
Stack(const Stack& st); //复制构造函数
~Stack() { delete[] items; }
bool isempty() { return top == 0; }
bool isfull() { return top == stacksize; }
bool push(const Type& item);
bool pop(Type& item);
Stack& operator=(const Stack& st);
private:
enum { SIZE = 10 };
int stacksize;
Type* items;
int top;
};
template <class Type>
Stack<Type>::Stack(int ss) : stacksize(ss), top(0)
{
items = new Type[stacksize];
}
template <class Type>
Stack<Type>::Stack(const Stack& st)
{
stacksize = st.stacksize;
top = st.top;
items = new Type[stacksize];
for (int i = 0; i < top; i++)
items[i] = st.items[i];
}
template <class Type>
bool Stack<Type>::push(const Type& item)
{
if (top < stacksize)
{
items[top++] = item;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::pop(Type& item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
template <class Type>
Stack<Type>& Stack<Type>::operator=(const Stack<Type>& st)
{
if (this == &st)
return *this;
delete[] items;
stacksize = st.stacksize;
top = st.top;
items = new Type[stacksize];
for (int i = 0; i < top; i++)
items[i] = st.items[i];
return *this;
}
#endif // !STCKTP1_H_
//程序清单 14.16
#include <iostream>
#include "stcktp1.h"
#include <cstdlib>
#include <ctime>
const int Num = 10;
int main(void)
{
srand(time(0));
cout << "Enter stack size: ";
int stacksize;
cin >> stacksize;
Stack<const char*> st(stacksize);
const char* in[Num] =
{
"1: Hank", "2: Kiki", "3: Betty", "4: Ian", "5: Wolfgang",
"6: Portia", "7: Joy", "8: Xaverie", "9: Juan", "10: Miasha"
};
const char* out[Num];
int processed = 0;
int nextin = 0;
while (processed < Num)
{
if (st.isempty())
st.push(in[nextin++]);
else if (st.isfull())
st.pop(out[processed++]);
else if (rand() % 2 && nextin < Num)
st.push(in[nextin++]);
else
st.pop(out[processed++]);
}
for (int i = 0; i < Num; i++)
cout << out[i] << endl;
cout << "Bye.\n";
return 0;
}
14.4.4 数组模板示例和非类型参数
//程序清单 14.17
#pragma once
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
#include <cstdlib>
using namespace std;
template <class T, int n>
class ArrayTP
{
public:
ArrayTP() {}
explicit ArrayTP(const T& v);
virtual T& operator[](int i);
virtual T operator[] (int i) const;
private:
T ar[n];
};
template <class T, int n>
ArrayTP<T, n>::ArrayTP(const T& v)
{
for (int i = 0; i < n; i++)
ar[i] = v;
}
template <class T, int n>
T& ArrayTP<T, n>::operator[](int i)
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i << " is out of range.\n";
exit(EXIT_FAILURE);
}
return ar[i];
}
template <class T, int n>
T ArrayTP<T, n>::operator[] (int i) const
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i << " is out of range.\n";
exit(EXIT_FAILURE);
}
return ar[i];
}
#endif // !ARRAYTP_H_
?template <class T, int n>
T为类型参数,int 为非类型或表达式参数
表达式参数可以是整型、枚举、引用和指针(例如double m 不合法,double &rm和double *pm合法)
模板代码不能修改参数的值,表达式的值必须是常量表达式
不同类不同对象
?ArrayTP<double, 12> eggweight;
?ArrayTP<double, 13> donuts;
同一类不同对象
Stack<int> egg(12);
Stack<int> dunkers(13);
14.4.5 模板多功能性
1.递归使用模板
ArrayTP<ArrayTP<int, 5>, 10> twodee;
等价于:
int twodee[10][5];
//程序清单 14.18
#include <iostream>
#include "arraytp.h"
int main(void)
{
ArrayTP<int, 10> sums;
ArrayTP<double, 10> aves;
ArrayTP<ArrayTP<int, 5>, 10> twodee;
int i, j;
for ( i = 0; i < 10; i++)
{
sums[i] = 0;
for ( j = 0; j < 5; j++)
{
twodee[i][j] = (i + 1) * (j + 1);
sums[i] += twodee[i][j];
}
aves[i] = (double)sums[i] / 10;
}
for ( i = 0; i < 10; i++)
{
for ( j = 0; j < 5; j++)
{
cout.width(2);
cout << twodee[i][j] << ' ';
}
cout << ": sum = ";
cout.width(3);
cout << sums[i] << ", average = " << aves[i] << endl;
}
cout << "Done.\n";
return 0;
}
?2.使用多个类型参数
//程序清单 14.19
#include <iostream>
#include <string>
using namespace std;
template <class T1, class T2>
class Pair
{
public:
Pair() {}
Pair(const T1& aval, const T2& bval) : a(aval), b(bval) {}
T1& first();
T2& second();
T1 first() const { return a; }
T2 second() const { return b; }
private:
T1 a;
T2 b;
};
template<class T1, class T2>
T1& Pair<T1, T2>::first()
{
return a;
}
template<class T1, class T2>
T2& Pair<T1, T2>::second()
{
return b;
}
int main(void)
{
Pair<string, int> ratings[4] =
{
Pair<string, int>("AAA", 4),
Pair<string, int>("BBB", 3),
Pair<string, int>("CCC", 2),
Pair<string, int>("DDD", 1)
};
int joints = sizeof(ratings) / sizeof(Pair<string, int>);
cout << "Rating:\t Eatery\n";
for (int i = 0; i < joints; i++)
cout << ratings[i].second() << ":\t" << ratings[i].first() << endl;
cout << "Oops! Revised rating:\n";
ratings[3].first() = "EEE";
ratings[3].second() = 5;
cout << ratings[3].second() << ":\t" << ratings[3].first() << endl;
return 0;
}
?3.默认类型模板参数
类模板的另一项新特性是可以为类型参数提供默认值:
template <class T1, class T2 = int>
class Topo { ... }
Topo<double, double> m1;? ? ? ? //T1 is double, T2 is double
Topo<doubel> m2;? ? ? ? ? ? ? ? ? ? ?//T1 is double, T2 is int
可以为类模板参数提供默认值,但不能为函数模板提供默认值
可以为非类型参数提供默认值,类模板和函数模板都适用
14.4.6 模板具体化
1.隐式实例化(用时再准备)
ArrayTP<int, 100> stuff;
编译器在需要对象前,不会生成类的隐式实例化
ArrayTP<double, 30> * pt;
pt = new ArrayTP<double, 30>;
2.显式实例化(不用也准备)
使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显式实例化
template class ArrayTP<string, 100>;
虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)
3.显式具体化(特殊类型特殊处理、特事特办)
4.部分具体化:
通用模板:template <class T1, class T2> class Pair { ... }
部分具体化模板:template <class T1> class Pair<T1, int>?{ ... },template后面是没有被具体化的类型参数
?
14.4.7 成员模板
模板可用作结构、类或模板类的成员。
//程序清单 14.20
#include <iostream>
using namespace std;
//模板类beta
template <typename T>
class beta
{
public:
beta(T t, int i) : q(t), n(i) {}
//模板函数blab
template <typename U>
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
void Show() const { q.show(); n.show(); }
private:
//模板类hold
template <typename V>
class hold
{
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
private:
V val;
};
hold <T> q;
hold <int> n;
};
int main(void)
{
beta<double> guy(3.5, 3);
cout << "T was set to double\n";
guy.Show();
cout << "V was set to T, which is double, then V was set to int\n";
cout << guy.blab(10, 2.3) << endl;
cout << "U was set to int.\n";
cout << guy.blab(10.0, 2.3) << endl;
cout << "U was set to double.\n";
return 0;
}
14.4.8 将模板用作参数
//程序清单 14.21
#include <iostream>
#include "stacktp.h"
using namespace std;
template <template <typename T> class thing>
class Crab
{
public:
Crab() {}
bool push(int a, double x) { return s1.push(a) && s2.push(x); }
bool pop(int& a, double& x) { return s1.pop(a) && s2.pop(x); }
private:
thing<int> s1;
thing<double> s2;
};
int main(void)
{
Crab<Stack> nebula;
int ni;
double nb;
cout << "Enter int double pairs, such as 4 3.5 (0 0 to end):\n";
while (cin >> ni >> nb && ni > 0 && nb > 0)
{
if (!nebula.push(ni, nb))
break;
}
while (nebula.pop(ni, nb))
cout << ni << ", " << nb << endl;
cout << "Done\n";
return 0;
}
14.4.9 模板类和友元
//程序清单 14.22
//非模板友元
#include <iostream>
using namespace std;
template<typename T>
class HasFriend
{
public:
HasFriend(const T& i) : item(i) { ct++; }
~HasFriend() { ct--; }
friend void counts();
//是所以HasFriend模板类的友元函数,HasFriend<int>和HasFriend<double>共用counts()
friend void reports(HasFriend<T>& ht);
private:
T item;
static int ct;
//相同类共用ct,类方法中初始化,HasFriend<int>和HasFriend<double>有不同ct
};
template <typename T>
int HasFriend<T>::ct = 0;
void counts()
{
cout << "int count: " << HasFriend<int>::ct << "; ";
cout << "double count: " << HasFriend<double>::ct << endl;
}
//非模板友元重载
void reports(HasFriend<int>& hf)
{
cout << "HasFriend<int>: " << hf.item << endl;
}
void reports(HasFriend<double>& hf)
{
cout << "HasFriend<double>: " << hf.item << endl;
}
int main(void)
{
cout << "No objects declared: ";
counts();
HasFriend<int> hfi1(10);
cout << "After hfi1 declared: ";
counts();
HasFriend<int> hfi2(20);
cout << "After hfi2 declared: ";
counts();
HasFriend<double> hfdb(10.5);
cout << "After hfdb declared: ";
counts();
reports(hfi1);
reports(hfi2);
reports(hfdb);
return 0;
}
//程序清单 14.23
//约束模板友元
#include <iostream>
using namespace std;
template<typename T> void counts();
template<typename T> void reports(T&);
template<typename TT>
class HasFriend
{
public:
HasFriend(const TT& i) : item(i) { ct++; }
~HasFriend() { ct--; }
friend void counts<TT>();
friend void reports<>(HasFriend<TT>& ht);
//或friend void reports<HasFriend>(HasFriend<TT>& ht);
private:
TT item;
static int ct;
//相同类共用ct,类方法中初始化,HasFriend<int>和HasFriend<double>有不同ct
};
template <typename T>
int HasFriend<T>::ct = 0;
//HasFriend<int>和HasFriend<double>有不同counts()
template <typename T>
void counts()
{
cout << "template size: " << sizeof(HasFriend<T>) << "; ";
cout << "template counts: " << HasFriend<T>::ct << endl;
}
template <typename T>
void reports(T& hf)
{
cout << hf.item << endl;
}
int main(void)
{
counts<int>();
HasFriend<int> hfi1(10);
HasFriend<int> hfi2(20);
HasFriend<double> hfdb(10.5);
reports(hfi1);
reports(hfi2);
reports(hfdb);
cout << "counts<int>() output:\n";
//模板函数具体化
counts<int>();
cout << "counts<double>() output:\n";
counts<double>();
return 0;
}
//程序清单 14.24
#include <iostream>
using namespace std;
template <typename T>
class ManyFriend
{
public:
ManyFriend(const T& i) : item(i) {}
template <typename C, typename D> friend void show2(C&, D&);
private:
T item;
};
template <typename C, typename D> void show2(C& c, D& d)
{
cout << c.item << ", " << d.item << endl;
}
int main(void)
{
ManyFriend<int> hfi1(10);
ManyFriend<int> hfi2(20);
ManyFriend<double> hfdb(10.5);
cout << "hfi1, hfi2: ";
show2(hfi1, hfi2);
cout << "hfi2, hfdb: ";
show2(hfi2, hfdb);
return 0;
}
14.4.10 模板别名
|