IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++_primer_plus学习笔记 第14章 C++中的代码重用 -> 正文阅读

[C++知识库]C++_primer_plus学习笔记 第14章 C++中的代码重用

本章内容包括:

  • 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 模板别名

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:02:42  更:2022-07-17 16:05:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:56:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码