第14章 C++中的代码重用
2020-02-24 10:31:14 8 举报
AI智能生成
C++ Plus 笔记
作者其他创作
大纲/内容
其他
初始化列表的初始化顺序
一般次序
调用基类构造函数,调用顺序按照他们被继承时声明的顺序(右到左)
对派生类新增的成员对象初始化,调用顺序按照他们在类中声明的顺序
执行派生类的构造函数体的顺序
例子
代码
#include<iostream>
using namespace std;
class Base1{
public:
Base1(int i){cout<<"constructing Base1 "<<i<<endl;}
};
class Base2{
public:
Base2(int j){cout<<"constructing Base2 "<<j<<endl;}
};
class Base3{
public:
Base3(){cout<<"constructing Base3* "<<endl;}
};
class Derived:public Base2,public Base1,public Base3{
public:
Derived(int a,int b,int c,int d):Base1(a),member2(d),member1(c),Base2(b)
{}
private:
Base1 member1;
Base2 member2;
Base3 member3;
};
int main()
{
Derived obj(1,2,3,4);
return 0;
}
结果
constructing Base2 2
constructing Base1 1
constructing Base3*
constructing Base1 3
constructing Base2 4
constructing Base3*
说明
与初始化顺序没有关系
指向指针的引用
http://blog.csdn.net/acs713/article/details/12508049
(1)指向指针的引用,不仅改变了指针所指的对象,也改变了指针本身。
(2)指向指针的引用,可以实现对传递给形参的实参数值的交换。即指向指针的引用可以修改指针的值,在参数传递时是传址。
包含对象成员的类
valarray类的简介
简介
是一个模板类
头文件
#include<valarray>
声明
1
valarray<double> q_values;
valarray<int> weights; // 长度为0的 int 数组
valarray<int> weights; // 长度为0的 int 数组
2
valarray<int> v2(8); // 长度为8的 int 数组
3
valarray<int> v3( 10 , 8 ) // an array of 8 int elements , each set to 10
4
double gap[5] = { 3.1, 3.5 , 3.8 , 2.9 , 3.3 }
valarray<int> v4( gpa, 4 ); // an array of 4 elements ,initialzed to the first 4 elements of gpa;
valarray<int> v4( gpa, 4 ); // an array of 4 elements ,initialzed to the first 4 elements of gpa;
C++11
valarray<int> v5 ={ 20, 32, 17, 9}; //可以使用初始化标
常用方法
v. xx()
v. xx()
operator[]()
能访问各个元素
size()
返回包含的元素数
sum()
返回所用元素的总和
max()
返回最大的元素
子主题
返回最小的元素
student 类设计
student 与
string,valarray关系
string,valarray关系
不是is-a,是has-a关系
is-a 关系——继承,获得继承接口和实现
has-a 关系——组合(包含),获得实现,不能获得接口
类设计
class student
{
private:
string name;
valarray<double> score;
....
}
{
private:
string name;
valarray<double> score;
....
}
name 、score都是分别是类string,valarray的对象
结果
能使用 string 和 valarray<double> 类的公有接口来访问和修改 name和scores对象
Student类没有继承string 和 valarray<double> 的公有接口
如果类的接口部分对新类有意义,可以..
例如:希望使用string接口中的operator<( ) 方法将Student对象按姓名进行排序,
为此可定义 Student::operator<( ) 成员函数,在内部使用函数string::operator<( )
为此可定义 Student::operator<( ) 成员函数,在内部使用函数string::operator<( )
student示例
总代码
类声明
// studentc.h -- defining a Student class using containment
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <string>
#include <valarray>
class Student
{
private:
typedef std::valarray<double> ArrayDb;
std::string name; // contained object
ArrayDb scores; // contained object
// private method for scores output
std::ostream & arr_out(std::ostream & os) const;
public:
Student() : name("Null Student"), scores() {}
explicit Student(const std::string & s)
: name(s), scores() {}
explicit Student(int n) : name("Nully"), scores(n) {}
Student(const std::string & s, int n)
: name(s), scores(n) {}
Student(const std::string & s, const ArrayDb & a)
: name(s), scores(a) {}
Student(const char * str, const double * pd, int n)
: name(str), scores(pd, n) {}
~Student() {}
double Average() const;
const std::string & Name() const;
double & operator[](int i);
double operator[](int i) const;
// friends
// input
friend std::istream & operator>>(std::istream & is,
Student & stu); // 1 word
friend std::istream & getline(std::istream & is,
Student & stu); // 1 line
// output
friend std::ostream & operator<<(std::ostream & os,
const Student & stu);
};
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <string>
#include <valarray>
class Student
{
private:
typedef std::valarray<double> ArrayDb;
std::string name; // contained object
ArrayDb scores; // contained object
// private method for scores output
std::ostream & arr_out(std::ostream & os) const;
public:
Student() : name("Null Student"), scores() {}
explicit Student(const std::string & s)
: name(s), scores() {}
explicit Student(int n) : name("Nully"), scores(n) {}
Student(const std::string & s, int n)
: name(s), scores(n) {}
Student(const std::string & s, const ArrayDb & a)
: name(s), scores(a) {}
Student(const char * str, const double * pd, int n)
: name(str), scores(pd, n) {}
~Student() {}
double Average() const;
const std::string & Name() const;
double & operator[](int i);
double operator[](int i) const;
// friends
// input
friend std::istream & operator>>(std::istream & is,
Student & stu); // 1 word
friend std::istream & getline(std::istream & is,
Student & stu); // 1 line
// output
friend std::ostream & operator<<(std::ostream & os,
const Student & stu);
};
类定义
//public methods
double Student::Average() const
{
if (scores.size() > 0)
return scores.sum()/scores.size();
else
return 0;
}
const string & Student::Name() const
{
return name;
}
double & Student::operator[](int i)
{
return scores[i]; // use valarray<double>::operator[]()
}
double Student::operator[](int i) const
{
return scores[i];
}
// private method
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)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << " empty array ";
return os;
}
// friends
// use string version of operator>>()
istream & operator>>(istream & is, Student & stu)
{
is >> stu.name;
return is;
}
// use string friend getline(ostream &, const string &)
istream & getline(istream & is, Student & stu)
{
getline(is, stu.name);
return is;
}
// use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
os << "Scores for " << stu.name << ":\n";
stu.arr_out(os); // use private method for scores
return os;
}
double Student::Average() const
{
if (scores.size() > 0)
return scores.sum()/scores.size();
else
return 0;
}
const string & Student::Name() const
{
return name;
}
double & Student::operator[](int i)
{
return scores[i]; // use valarray<double>::operator[]()
}
double Student::operator[](int i) const
{
return scores[i];
}
// private method
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)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << " empty array ";
return os;
}
// friends
// use string version of operator>>()
istream & operator>>(istream & is, Student & stu)
{
is >> stu.name;
return is;
}
// use string friend getline(ostream &, const string &)
istream & getline(istream & is, Student & stu)
{
getline(is, stu.name);
return is;
}
// use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
os << "Scores for " << stu.name << ":\n";
stu.arr_out(os); // use private method for scores
return os;
}
使用
#include <iostream>
#include "studentc.h"
using std::cin;
using std::cout;
using std::endl;
void set(Student & sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main()
{
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 << "\nResults:";
for (i = 0; i < pupils; ++i)
{
cout << endl << ada[i];
cout << "average: " << ada[i].Average() << endl;
}
cout << "Done.\n";
// cin.get();
return 0;
}
void set(Student & sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa);
cout << "Please enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
#include "studentc.h"
using std::cin;
using std::cout;
using std::endl;
void set(Student & sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main()
{
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 << "\nResults:";
for (i = 0; i < pupils; ++i)
{
cout << endl << ada[i];
cout << "average: " << ada[i].Average() << endl;
}
cout << "Done.\n";
// cin.get();
return 0;
}
void set(Student & sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa);
cout << "Please enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
说明
类声明/类定义
重命名
typedef std::valarray<double> ArrayDb;
可以用ArrayDb来替代valarray<double>
构造函数
explicit Student(const std::string & s): name(s), scores() {}
explicit Student(int n) : name("Nully"), scores(n) {}
explicit Student(int n) : name("Nully"), scores(n) {}
使用explict关闭隐式转换,使编译阶段出现错误优于在运行阶段出现错误
说明:
Student stu1="sally"; x
Student stu2;
stu2=9; x
//不可以。
Student stu1= Student("sally");
Stu2 = Student(9); 才可以。
Student stu1="sally"; x
Student stu2;
stu2=9; x
//不可以。
Student stu1= Student("sally");
Stu2 = Student(9); 才可以。
Student(const std::string & s, int n)
: name(s), scores(n) {}
: name(s), scores(n) {}
scores(n)表示数组的大小为n
Student(const std::string & s, const ArrayDb & a)
: name(s), scores(a) {}
: name(s), scores(a) {}
scores(a)表示初始化数组为a
Student(const char * str, const double * pd, int n)
: name(str), scores(pd, n) {}
: name(str), scores(pd, n) {}
scores(pd,n)表示前n个元素初始化为数组pd
成员函数
// private method for scores output
std::ostream & arr_out(std::ostream & os) const;
std::ostream & arr_out(std::ostream & os) const;
输出成员数据的valarray中的数据。
由友元函数operator<<( ) 引用
由友元函数operator<<( ) 引用
double Average() const;
计算并返回平均值
const std::string & Name() const;
返回名字
double & operator[](int i);
double operator[](int i) const;
double operator[](int i) const;
不理解为什么两个都可以存在??
第一个可以修改valarray<>数组中的值
第二个不可以修改
第二个不可以修改
Student stu1;
stu1[0]、stu1[1]、表示各科的成绩。
stu1[0]、stu1[1]、表示各科的成绩。
友元函数
friend std::istream & operator>>(std::istream & is, Student & stu); // 1 word
Student stu2;
cin>>stu2;表示输入stu2中的name成员。//只能是一个单词
至于valarray<>数组中的元素,可以直接
cin>>stu2[n];来直接表示输入
cin>>stu2;表示输入stu2中的name成员。//只能是一个单词
至于valarray<>数组中的元素,可以直接
cin>>stu2[n];来直接表示输入
friend std::istream & getline(std::istream & is,Student & stu); // 1 line
Student stu2;
getline(cin,stu2);//表示输入一行的字符串给stu2对象中的name成员数据
getline(cin,stu2);//表示输入一行的字符串给stu2对象中的name成员数据
friend std::ostream & operator<<(std::ostream & os,
const Student & stu);
const Student & stu);
Student stu2;
...
cout<<stu2;//输出对象的成员数据
...
cout<<stu2;//输出对象的成员数据
使用
建立对象数组,注意,ada[][] 表示的是成绩。
其他说明
初始化顺序
C++紫皮书,整理
使用被包含都对象的接口
例如,需要用到valarray中的sum()函数,
可以在声明一个公有函数,用scores中的接口
可以在声明一个公有函数,用scores中的接口
私有继承
描述关系
也是has-a关系,获得实现,不获得接口
特点
基类的公有成员和保护成员将成为派生类的私有成员,
基类的私有成员派生类不能直接访问
基类的私有成员派生类不能直接访问
Student实例(私有继承版本)
总代码
类声明
// studenti.h -- defining a Student class using private inheritance
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <valarray>
#include <string>
class Student : private std::string, private std::valarray<double>
{
private:
typedef std::valarray<double> ArrayDb;
// private method for scores output
std::ostream & arr_out(std::ostream & os) const;
public:
Student() : std::string("Null Student"), ArrayDb() {}
explicit Student(const std::string & s)
: std::string(s), ArrayDb() {}
explicit Student(int n) : std::string("Nully"), ArrayDb(n) {}
Student(const std::string & s, int n)
: std::string(s), ArrayDb(n) {}
Student(const std::string & s, const ArrayDb & a)
: std::string(s), ArrayDb(a) {}
Student(const char * str, const double * pd, int n)
: std::string(str), ArrayDb(pd, n) {}
~Student() {}
double Average() const;
double & operator[](int i);
double operator[](int i) const;
const std::string & Name() const;
// friends
// input
friend std::istream & operator>>(std::istream & is,
Student & stu); // 1 word
friend std::istream & getline(std::istream & is,
Student & stu); // 1 line
// output
friend std::ostream & operator<<(std::ostream & os,
const Student & stu);
};
#endif
类定义
// studenti.cpp -- Student class using private inheritance
#include "studenti.h"
using std::ostream;
using std::endl;
using std::istream;
using std::string;
// public methods
double Student::Average() const
{
if (ArrayDb::size() > 0)
return ArrayDb::sum()/ArrayDb::size();
else
return 0;
}
const string & Student::Name() const
{
return (const string &) *this;
}
double & Student::operator[](int i)
{
return ArrayDb::operator[](i); // use ArrayDb::operator[]()
}
double Student::operator[](int i) const
{
return ArrayDb::operator[](i);
}
// private method。的金额
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)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << " empty array ";
return os;
}
// friends
// use String version of operator>>()
istream & operator>>(istream & is, Student & stu)
{
is >> (string &)stu;
return is;
}
// use string friend getline(ostream &, const string &)
istream & getline(istream & is, Student & stu)
{
getline(is, (string &)stu);
return is;
}
// use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
os << "Scores for " << (const string &) stu << ":\n";
stu.arr_out(os); // use private method for scores
return os;
}
使用
#include <iostream>
#include "studenti.h"
using std::cin;
using std::cout;
using std::endl;
void set(Student & sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main()
{
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 << "\nResults:";
for (i = 0; i < pupils; i++)
{
cout << endl << ada[i];
cout << "average: " << ada[i].Average() << endl;
}
cout << "Done.\n";
// cin.get();
return 0;
}
void set(Student & sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa);
cout << "Please enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
说明
类声明/类定义
构造函数
组合:
Student(const std::string & s, const ArrayDb & a)
: name(s), scores(a) {}
私有继承:
Student(const std::string & s, int n)
: std::string(s), ArrayDb(n) {}
Student(const std::string & s, const ArrayDb & a)
: name(s), scores(a) {}
私有继承:
Student(const std::string & s, int n)
: std::string(s), ArrayDb(n) {}
说明
因为私有继承没有具体名,所以直接调用基类的函数。
ArrayDb是std::valarray<double>的别名
ArrayDb是std::valarray<double>的别名
成员函数
1
(调用基类的方法)
(调用基类的方法)
组合关系
double Student::Average() const
{
if (scores.size() > 0)
return scores.sum()/scores.size();
else
return 0;
}
{
if (scores.size() > 0)
return scores.sum()/scores.size();
else
return 0;
}
私有继承
return ArrayDb::sum()/ArrayDb::size();
说明
另外如果派生类中有同名函数(不管参数或者其他是否不同),
需要有作用限定域符号。
如果没有同名函数,可以不用作用限定域,上其实可以不使用。//??
还是因为是私有继承所以要限定域??
需要有作用限定域符号。
如果没有同名函数,可以不用作用限定域,上其实可以不使用。//??
还是因为是私有继承所以要限定域??
2
(返回基类的私有数据)
(返回基类的私有数据)
组合关系
string
const string & Student::Name() const
{
return name;
}
{
return name;
}
arrayDb
double & Student::operator[](int i)
{
return scores[i]; // use valarray<double>::operator[]()
}
{
return scores[i]; // use valarray<double>::operator[]()
}
私有继承
string
const string & Student::Name() const
{
return (const string &) *this;
}
arrayDb
double & Student::operator[](int i)
{
return ArrayDb::operator[](i); // use ArrayDb::operator[]()
}
说明
return (const string &) *this;表示返回派生类的基类string部分
return ArrayDb::operator[](i); //调用ArrayDb的operator[]函数
友元函数
组合关系
istream & operator>>(istream & is, Student & stu)
{
is >> stu.name;
return is;
}
{
is >> stu.name;
return is;
}
- 有对象名,所以可以使用通过对象名使用函数。
- 继承只能通过调用基类的函数,但是友元类不属于类,不能用类名显式地限定函数名。
- 友元函数可以访问派生类的私有数据成员,当然也可以访问基类的公有、保护成员
私有继承
istream & operator>>(istream & is, Student & stu)
{
is >> (string &)stu;
return is;
}
os<<( string &)stu;//输出string的name
说明
os<<( string &)stu;将stu转化为string对象引用,
进而调用函数operator<<(ostream &,const String &)
进而调用函数operator<<(ostream &,const String &)
使用
和组合差不多
(选择)has-a关系
使用包含还是私有继承
使用包含还是私有继承
一般使用包含来建立has-a关系
如果新类的需要访问原有类的保护成员,
或需要重新定义虚函数,则应使用继承。
如果新类的需要访问原有类的保护成员,
或需要重新定义虚函数,则应使用继承。
保护继承
特点
基类的公有成员和保护成员将成为派生类的保护成员,
基类的私有成员派生类不能直接访问。
基类的私有成员派生类不能直接访问。
与私有继承区别
区别体现在,从派生类中派生另一个类时。
各种继承方式区别
表格
特征 | 公有继承 | 保护继承 | 私有继承 |
---|---|---|---|
公有成员变成 | 派生类的公有成员 | 派生类的保护成员 | 派生类的私有成员 |
保护成员变成 | 派生类的保护成员 | 派生类的保护成员 | 派生类的私有成员 |
私有成员变成 | 只能通过基类接口访问 | 只能通过基类接口访问 | 只能通过基类接口访问 |
能否隐式向上转换 | 是 | 是(但只能在派生类中) | 否 |
说明
私有继承不能隐式向上转换原因:
(隐式向上转换说明基类的指针或引用指向派生类对象)
(隐式向上转换说明基类的指针或引用指向派生类对象)
可以的话,就相当于调用了基类接口。
为什么不能在派生类中?
在派生类中使用隐式
向上转换:
向上转换:
Student::Student(Student & stu)
:basestudent(stu)
???相当于这样吗?觉得私有继承应该也可以这样做啊
:basestudent(stu)
???相当于这样吗?觉得私有继承应该也可以这样做啊
(私有继承、保护继承)
使用using重新定义访问权限
使用using重新定义访问权限
问题
使Student能够使用valarray类的sum()方法
方法一
在Student类中声明一个sum()方法
double Student::sum() const
{
return std::valarray<double>::sum();
}
{
return std::valarray<double>::sum();
}
方法二
using声明来指出派生类可以使用特定的基类成员,
即使采用的是私有派生。
在派生类的公有部分声明
即使采用的是私有派生。
在派生类的公有部分声明
class Student:private std::string,private std::valarrat<double>
{
....
public:
using std::valarray<double>::sum();
}
{
....
public:
using std::valarray<double>::sum();
}
多重继承(MI)
描述关系
公有MI
is-a关系
私有、保护MI
has-a关系
类声明格式
class singingWaiter:public Waiter, public Singer{ ... };
说明
class singingWaiter:public Waiter, Singer{ ... };
//Singer 前没有public,会认为Singer是私有继承
//Singer 前没有public,会认为Singer是私有继承
Mi问题(主要)
从两个不同基类继承同名方法
从两个或更多相关基类那里继承同一个类的多个实例
实例//worker0
(没有派生singerwaiter)
(没有派生singerwaiter)
类声明
// worker0.h -- working classes
#ifndef WORKER0_H_
#define WORKER0_H_
#include <string>
class Worker // an abstract base class
{
private:
std::string fullname;
long id;
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const std::string & s, long n)
: fullname(s), id(n) {}
virtual ~Worker() = 0; // pure virtual destructor
virtual void Set();
virtual void Show() const;
};
class Waiter : public Worker
{
private:
int panache;
public:
Waiter() : Worker(), panache(0) {}
Waiter(const std::string & s, long n, int p = 0)
: Worker(s, n), panache(p) {}
Waiter(const Worker & wk, int p = 0)
: Worker(wk), panache(p) {}
void Set();
void Show() const;
};
class Singer : public Worker
{
protected:
enum {other, alto, contralto, soprano,
bass, baritone, tenor};
enum {Vtypes = 7};
private:
static char *pv[Vtypes]; // string equivs of voice types
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const std::string & s, long n, int v = other)
: Worker(s, n), voice(v) {}
Singer(const Worker & wk, int v = other)
: Worker(wk), voice(v) {}
void Set();
void Show() const;
};
#endif
类定义
// worker0.cpp -- working class methods
#include "worker0.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
// Worker methods
// must implement virtual destructor, even if pure
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 << "\n";
cout << "Employee ID: " << id << "\n";
}
// Waiter methods
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 << "\n";
}
// Singer methods
char * Singer::pv[] = {"other", "alto", "contralto",
"soprano", "bass", "baritone", "tenor"};
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 ( i % 4 == 3)
cout << endl;
}
if (i % 4 != 0)
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::Show() const
{
cout << "Category: singer\n";
Worker::Show();
cout << "Vocal range: " << pv[voice] << endl;
}
使用
// worktest.cpp -- test worker class hierarchy
#include <iostream>
#include "worker0.h"
const int LIM = 4;
int main()
{
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();
for (i = 0; i < LIM; i++)
{
pw[i]->Show();
std::cout << std::endl;
}
// std::cin.get();
return 0;
}
说明
构造函数里面,不要直接cin>>,
此例中,set()成员函数可以,但千万不要出现在构造函数中。
此例中,set()成员函数可以,但千万不要出现在构造函数中。
i%4==3 表示每四个数,加一endl;
后if( i%4 !=0 ) cout<<endl;表示如果总输出不是4的倍数,也要输出空行
后if( i%4 !=0 ) cout<<endl;表示如果总输出不是4的倍数,也要输出空行
1 2 3 4
5 6 7 8
9 10
....
5 6 7 8
9 10
....
如果从waiter、singer
上派生出singerwaiter
上派生出singerwaiter
类声明
class SingerWaiter : public Singer , public Waiter { ... }
问题
有多少个worker?
保留两个Worker副本
SingerWaiter ed;
Worker *pw = &ed; // ambiguous,不允许
Worker *pw = &ed; // ambiguous,不允许
Worker *pw1=(Waiter *) ed;
Worker *pw2=(Waiter *)ed;
Worker *pw2=(Waiter *)ed;
哪个方法?
(有多少个worker?)
虚基类
虚基类
解决问题
从两个或更多相关基类那里继承同一个类的多个实例
使SingerWaiter对象只包含Worker对象的一个副本。
虚基类定义
class Woker;
class Waiter: virtual public Worker
class Singer: virtual public Worker
class SingerWaiter: public Waiter, public Singer
class Waiter: virtual public Worker
class Singer: virtual public Worker
class SingerWaiter: public Waiter, public Singer
疑问
(不要看了)
(不要看了)
为什么使用术语虚?
与虚函数没有什么联系,引进新关键字会很麻烦,所以“重载”虚关键字
为什么不抛弃将基类声明为虚的这种方式,使虚行为成为多MI的准则
(更像问为什么不抛弃不声明为虚的方式)
(更像问为什么不抛弃不声明为虚的方式)
可能需要基类的多个拷贝
将基类作为虚的要求程序完成额外的工作
有其缺点(。。)
是否存在麻烦?
必须以不同的方式编写一些代码。eg:在worker ,singer前加virtual
新的构造函数规则
问题
单继承
代码
class A{
int a;
public:
A(int n = 0) :a(n){}
....
};
class B :public A{
int b;
public:
B(int m = 0; int n = 0 ): A(n),b(m){}
....
};
class C :public B{
int c;
public:
C(int q = 0,int m = 0,int n = 0):B(m,n),c(q){}
.....
};
说明
C-->B(m,n)-->B-->A(n)-->A ,传递性
基类是虚时候
SingerWaiter(const Worker & wk , int p=0 , int v = Singer::other)
:Waiter( wk,p),Singer(wk,v){}//flawed
:Waiter( wk,p),Singer(wk,v){}//flawed
- 不起作用,因为将通过两种不同的途径(worker 和 singer)将wk传递给Worker对象。
- C++基类为虚时,禁止信息通过中间类自动传递给基类,所以上述构造函数将初始化
- 但又必须派生对象之前构造基类对象组件。
构造方式
SingingWaiter( const Worker &wk,int p = 0 , int v = Singer::other)
:Worker(wk),Waiter(wk,p),Singer(wk,v)
:Worker(wk),Waiter(wk,p),Singer(wk,v)
说明
- 对虚基类,除非只需使用该虚基类的默认构造函数,这种用法
- 对非虚基类,这种用法是不正确的。
(哪个方法?)
1
问题
Singer 中有show()方法
Waiter中有show()方法
SingerWaiter newhire( "Elise Hawks" , 2005 , 6 , soprano);
newhire.Show( );//ambiguous
Waiter中有show()方法
SingerWaiter newhire( "Elise Hawks" , 2005 , 6 , soprano);
newhire.Show( );//ambiguous
解决
1
作用域解析运算
newhire.Singer::Show( ) ;
newhire.Waiter::Show( ) ;
newhire.Waiter::Show( ) ;
2
在SingerWaiter中
重新定义Show( )
//指明使用哪个版本
重新定义Show( )
//指明使用哪个版本
void SingingWaiter::Show()
{
Singer::Show();
}
{
Singer::Show();
}
2
问题
void SingingWaiter::Show()
{
Singer::Show();
Waiter::Show();
}
//会调用两次Worker::Show(),信息混乱
{
Singer::Show();
Waiter::Show();
}
//会调用两次Worker::Show(),信息混乱
void Singer::Show() const
{
cout << "Category: singer\n";
Worker::Show();
cout << "Vocal range: " << pv[voice] << endl;
}
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Show();
cout << "Panache rating: " << panache << "\n";
}
void Worker::Show() const
{
cout << "Name: " << fullname << "\n";
cout << "Employee ID: " << id << "\n";
}
解决
加一个属于保护,只显示自身
(新增)数据的data()方法。
//派生类例外,显示两个直接基类数据
的data()方法(其实可以不定义吧)
(新增)数据的data()方法。
//派生类例外,显示两个直接基类数据
的data()方法(其实可以不定义吧)
void Worker::Data() const
{
cout << "Name: " << fullname << endl;
cout << "Employee ID: " << id << endl;
}
子主题
void Waiter::Data() const
{
cout << "Panache rating: " << panache << endl;
}
//public
void Waiter::Show() const
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Data();
Data();
}
void Singer::Data() const
{
cout << "Vocal range: " << pv[voice] << endl;
}
//public
void Waiter::Show() const
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Data();
Data();
}
void SingingWaiter::Data() const
{
Singer::Data();
Waiter::Data();
}
//public
void Waiter::Show() const
void Waiter::Show() const
{
cout << "Category: waiter\n";
Worker::Data(); // 可以直接用!!不要忘记了
Data();
}
其他
Set()函数也一样,处理方法相同
实例
(解决了问题的实例)
(解决了问题的实例)
类声明
// workermi.h -- working classes with MI
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <string>
class Worker // an abstract base class
{
private:
std::string fullname;
long id;
protected:
virtual void Data() const;
virtual void Get();
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const std::string & s, long n)
: fullname(s), id(n) {}
virtual ~Worker() = 0; // pure virtual function
virtual void Set() = 0;
virtual void Show() const = 0;
};
class Waiter : virtual public Worker
{
private:
int panache;
protected:
void Data() const;
void Get();
public:
Waiter() : Worker(), panache(0) {}
Waiter(const std::string & s, long n, int p = 0)
: Worker(s, n), panache(p) {}
Waiter(const Worker & wk, int p = 0)
: Worker(wk), panache(p) {}
void Set();
void Show() const;
};
class Singer : virtual public Worker
{
protected:
enum {other, alto, contralto, soprano,
bass, baritone, tenor};
enum {Vtypes = 7};
void Data() const;
void Get();
private:
static char *pv[Vtypes]; // string equivs of voice types
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const std::string & s, long n, int v = other)
: Worker(s, n), voice(v) {}
Singer(const Worker & wk, int v = other)
: Worker(wk), voice(v) {}
void Set();
void Show() const;
};
// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
protected:
void Data() const;
void Get();
public:
SingingWaiter() {}
SingingWaiter(const std::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 & wt, int p = 0)
: Worker(wt),Waiter(wt,p), Singer(wt) {}
void Set();
void Show() const;
};
#endif
类定义
// workermi.h -- working classes with MI
#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <string>
class Worker // an abstract base class
{
private:
std::string fullname;
long id;
protected:
virtual void Data() const;
virtual void Get();
public:
Worker() : fullname("no one"), id(0L) {}
Worker(const std::string & s, long n)
: fullname(s), id(n) {}
virtual ~Worker() = 0; // pure virtual function
virtual void Set() = 0;
virtual void Show() const = 0;
};
class Waiter : virtual public Worker
{
private:
int panache;
protected:
void Data() const;
void Get();
public:
Waiter() : Worker(), panache(0) {}
Waiter(const std::string & s, long n, int p = 0)
: Worker(s, n), panache(p) {}
Waiter(const Worker & wk, int p = 0)
: Worker(wk), panache(p) {}
void Set();
void Show() const;
};
class Singer : virtual public Worker
{
protected:
enum {other, alto, contralto, soprano,
bass, baritone, tenor};
enum {Vtypes = 7};
void Data() const;
void Get();
private:
static char *pv[Vtypes]; // string equivs of voice types
int voice;
public:
Singer() : Worker(), voice(other) {}
Singer(const std::string & s, long n, int v = other)
: Worker(s, n), voice(v) {}
Singer(const Worker & wk, int v = other)
: Worker(wk), voice(v) {}
void Set();
void Show() const;
};
// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
protected:
void Data() const;
void Get();
public:
SingingWaiter() {}
SingingWaiter(const std::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 & wt, int p = 0)
: Worker(wt),Waiter(wt,p), Singer(wt) {}
void Set();
void Show() const;
};
#endif
使用
代码
// workmi.cpp -- multiple inheritance
// compile with workermi.cpp
#include <iostream>
#include <cstring>
#include "workermi.h"
const int SIZE = 5;
int main()
{
using std::cin;
using std::cout;
using std::endl;
using std::strchr;
Worker * lolas[SIZE];
int ct;
for (ct = 0; ct < SIZE; ct++)
{
char choice;
cout << "Enter the employee category:\n"
<< "w: waiter s: singer "
<< "t: singing waiter q: quit\n";
cin >> choice;
while (strchr("wstq", choice) == NULL)
{
cout << "Please enter a w, s, t, or q: ";
cin >> choice;
}
if (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;
}
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";
// cin.get();
// cin.get();
return 0;
}
说明
while (strchr("wstq", choice) == NULL)
//返回choice 在字符串"wstq"中第一次出现的位置
//返回choice 在字符串"wstq"中第一次出现的位置
case 'w': 记住要单引号
Mi的其它问题
混合使用虚基类
和非虚基类
和非虚基类
假设B被用作C和D的虚基类,
B被用作X和Y的非虚基类,
类M,从C,D,X,Y中派生而来,
问:洋浦多少个B类子对象?
B被用作X和Y的非虚基类,
类M,从C,D,X,Y中派生而来,
问:洋浦多少个B类子对象?
3个,
类M从虚派生祖先(即类C和D)那里共继承一个B类子对象
并从每一个非虚派生祖先(即类X和Y)分别继承了一个B类子对象
类M从虚派生祖先(即类C和D)那里共继承一个B类子对象
并从每一个非虚派生祖先(即类X和Y)分别继承了一个B类子对象
虚基类和支配
(二义性问题)
(二义性问题)
问题
不是虚基类
class B
{
public:
short q();
...
};
class C:public B
{
public:
long q();
....
};
class F:public B
{
...
};
F a;
a.q();//ambiguous
a.q();//ambiguous
虚基类
class B
{
public:
short q();
...
};
class C:virtual public B
{
public:
long q();
....
};
class F:public B
{
...
};
F a;
a.q();//valid
C中的q()优先于B中的q();
a.q();//valid
C中的q()优先于B中的q();
说明
使用虚基类时,如果某个名称优先于其他所有名称,名称
不会产生二义性。
不会产生二义性。
虚二义性规则与访问规则无关
class B
{
public:
short q();
...
};
class C:virtual public B
{
private://变为私有
long q();
....
};
class F:public B
{
...
};
不带作用域解析运算符的q() 在F类中或类外都表示为B::q()
F a;
a.q();//相当于访问了不可访问的q(),所以是不行的。
F a;
a.q();//相当于访问了不可访问的q(),所以是不行的。
类模板
作用
提供参数化类型,即能够将类型名作为参数传递给接收方来建立类或函数。
定义、使用
类声明
代码
原来
typedef unsigned long Item;
class Stack
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Item items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty() const;
bool isfull() const;
bool push(const Item & item); // add item to stack
bool pop(Item & item); // pop top into item
};
现在
template <class Type>
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Type items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & item); // add item to stack
bool pop(Type & item); // pop top into item
};
说明
template<class Type>
class Stack{
.....
};
class Stack{
.....
};
要以template<..>开头,可以把class 看做是变量的类型名,该变量接收类型作为其值.
class可用typename 代替
Type可以使用自己的泛型名代替,一般用T,Type
Item items[MAX](原来)——(变成)Type items[Max]
其他函数
其他函数
对于Stack来说,意味着应将声明中所用typedef标识符Item
替换成Type
替换成Type
类定义
代码
template <class Type>
Stack<Type>::Stack()
{
top = 0;
}
template <class Type>
bool Stack<Type>::isempty()
{
return top == 0;
}
template <class Type>
bool Stack<Type>::isfull()
{
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;
}
说明
bool Stack::push( const Item &item)
{
....
}
{
....
}
//改为
template<class Type>
bool Stack<Type>::push(const Type & item)
{
.....
}
template<class Type>
bool Stack<Type>::push(const Type & item)
{
.....
}
template<class Type>前缀
Stack<Type>限定符
Stack<Type>限定符
如果在类声明中定义了方法(内联定义),可以省略模板前缀和类限定符
模板的具体实现——如用来处理string对象的栈类称为实例化或具体化
模板不是函数,不能单独编译。一般将所用模板信息放在一个头文件中
使用模板类
代码
// stacktem.cpp -- testing the template stack class
#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"
using std::cin;
using std::cout;
int main()
{
Stack<std::string> st; // create an empty stack
char ch;
std::string po;
cout << "Please enter A to add a purchase order,\n"
<< "P to process a PO, or Q to quit.\n";
while (cin >> ch && std::toupper(ch) != 'Q')
{
while (cin.get() != '\n')
continue;
if (!std::isalpha(ch))
{
cout << '\a';
continue;
}
switch(ch)
{
case 'A':
case 'a': cout << "Enter a PO number to 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;
}
}
cout << "Please enter A to add a purchase order,\n"
<< "P to process a PO, or Q to quit.\n";
}
cout << "Bye\n";
// cin.get();
// cin.get();
return 0;
}
原理
仅在程序包含模板不能生成模板类,必须请求实例化。
Stack<int> kernels;
Stack<string> colonels;
看到上述声明,编译器按Stack<Type>模板生成两个独立的类声明和两组独立的类方法。
Stack<int>使用Int替换模板中所用的Type
Stack<string> colonels;
看到上述声明,编译器按Stack<Type>模板生成两个独立的类声明和两组独立的类方法。
Stack<int>使用Int替换模板中所用的Type
说明
与函数模板不同,类模板必须显式地提供所需的类型。
指针栈
(小难点)
(小难点)
应用
功能
某人把一车文件交给Plodson,如果Plodson的收取篮(in-basket)是空的,他将取出车中最上面的文件夹,
将其放入收取篮;如果收取篮是满的,plodson将取出篮中最上面的文件,对它进行处理,然后放入发出篮
(outbasket);如果收取篮既不是空,也不是满,plodson可能取出篮中最上面的文件进行处理,也可能取出
车中下一个文件,放在收取篮中。
将其放入收取篮;如果收取篮是满的,plodson将取出篮中最上面的文件,对它进行处理,然后放入发出篮
(outbasket);如果收取篮既不是空,也不是满,plodson可能取出篮中最上面的文件进行处理,也可能取出
车中下一个文件,放在收取篮中。
代码
头文件
// stcktp1.h -- modified Stack template
#ifndef STCKTP1_H_
#define STCKTP1_H_
template <class Type>
class Stack
{
private:
enum {SIZE = 10}; // default size
int stacksize;
Type * items; // holds stack items
int top; // index for top stack item
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); // add item to stack
bool pop(Type & item); // pop top into item
Stack & operator=(const Stack & st);
};
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
使用
// stkoptr1.cpp -- testing stack of pointers
#include <iostream>
#include <cstdlib> // for rand(), srand()
#include <ctime> // for time()
#include "stcktp1.h"
const int Num = 10;
int main()
{
std::srand(std::time(0)); // randomize rand()
std::cout << "Please enter stack size: ";
int stacksize;
std::cin >> stacksize;
// create an empty stack with stacksize slots
Stack<const char *> st(stacksize);
// in basket
const char * in[Num] = {
" 1: Hank Gilgamesh", " 2: Kiki Ishtar",
" 3: Betty Rocker", " 4: Ian Flagranti",
" 5: Wolfgang Kibble", " 6: Portia Koop",
" 7: Joy Almondo", " 8: Xaverie Paprika",
" 9: Juan Moore", "10: Misha Mache"
};
// out basket
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 (std::rand() % 2 && nextin < Num) // 50-50 chance
st.push(in[nextin++]);
else
st.pop(out[processed++]);
}
for (int i = 0; i < Num; i++)
std::cout << out[i] << std::endl;
std::cout << "Bye\n";
// std::cin.get();
// std::cin.get();
return 0;
}
说明
Type items[MAX]改为Type * items 是想让栈的大小可变化,
因此也新增了一个数据成员stacksize
因此也新增了一个数据成员stacksize
Type *items
....
items = new Type[stacksize];//items 是指向数组的指针,items[]也可以照常使用
....
items = new Type[stacksize];//items 是指向数组的指针,items[]也可以照常使用
items = new char *[stacksize]
表示数组是指向char*的指针数组,
表示数组是指向char*的指针数组,
函数的参数里面。
指向指针的引用
char *&
指向指针的引用
char *&
http://blog.csdn.net/acs713/article/details/12508049
特殊性
(1)指向指针的引用,不仅改变了指针所指的对象,也改变了指针本身。
(2)指向指针的引用,可以实现对传递给形参的实参数值的交换。即指向指针的引用可以修改指针的值,在参数传递时是传址。
把字符串压入栈实际上是把字符串的地址压入到栈中。
从栈中弹出字符串是把地址值复制到out数组中。
从栈中弹出字符串是把地址值复制到out数组中。
数组模板示例
和非类型的参数
和非类型的参数
允许指定数组大小
的简单数组模板
的简单数组模板
方法1
在类中使用动态数组和构造函数参数来提供元素数目,如上的stack
方法2
使用模板参数来提供常规数组大小
方法2代码
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
#include <cstdlib>
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & v);
virtual T & operator[](int i);
virtual T operator[](int i) const;
};
template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
for (int i = 0; i < n; i++)
ar[i] = v; //把前n个数赋值为数值v;
}
template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
if (i < 0 || i >= n)
{
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
template <class T, int n>
T ArrayTP<T,n>::operator[](int i) const
{
if (i < 0 || i >= n)
{
std::cerr << "Error in array limits: " << i
<< " is out of range\n";
std::exit(EXIT_FAILURE);
}
return ar[i];
}
#endif
说明
template<class T , int n>
T ——类型参数
n——非类型参数/表达式参数//int n, n指明了类型
表达式参数限制
表达式参数可以是整型、枚举、引用或者指针。
double m ,即template<class T ,double m>是不合法的。
但double *rm,double *pm是合法的
double m ,即template<class T ,double m>是不合法的。
但double *rm,double *pm是合法的
模板代码不能修改参数的值,也不能使用参数的地址,所用ArrayTP中,
n++,&n是非法的。
实例化模板时(eg: ArrayTP<double,12> eggweights),表达式参数
的值必须是常量表达式。//不可以是变量?
n++,&n是非法的。
实例化模板时(eg: ArrayTP<double,12> eggweights),表达式参数
的值必须是常量表达式。//不可以是变量?
与方法一对比
好处
构造函数方法(方法一)使用的是通过new 和delete管理的堆内存,表达式参数方法使用
的是为自动变量维护的内存栈,执行速度快
的是为自动变量维护的内存栈,执行速度快
坏处
每种数组大小都声明自己的模板
//下列生成两个独立的类声明
ArrayTP<double , 12>eggweights;
ArrayTP<double , 13>donuts;
ArrayTP<double , 12>eggweights;
ArrayTP<double , 13>donuts;
//下面的声明只生成一个类声明
Stack<int>eggs(12);
Stack<int>dunkers(13);
Stack<int>eggs(12);
Stack<int>dunkers(13);
构造函数方法(方法2)更通用,它可以将一种尺寸的数组赋给另一种尺寸的数组,
也允许改变数组的大小。
也允许改变数组的大小。
virtual T & operator[](int i);——*1
virtual T operator[](int i) const;——*2
//区别?
//区别?
第一个返回的ar[i]可以作为左值,可以修改ar[i](虽然是私有成员)的内容。(因为返回的是a[i]的引用)
模板的多功能性
模板的用法
用作基类
template<typename T>
class Array{
private:
T entry;
.....
};
class Array{
private:
T entry;
.....
};
template<typename Type>
clsee GrowArray:public Array<Type>{...};
clsee GrowArray:public Array<Type>{...};
!!!注意写法
组件类
template<typename TP>
class Stack
{
Array<Tp>ar;
......
}
class Stack
{
Array<Tp>ar;
......
}
用作其他模板
的类型参数
的类型参数
Array< Stack<int> >asi;
//下详细
//下详细
C++98要求至少要用一个空白字符将两个>分开以区分>>,
C++11不要求这样做
C++11不要求这样做
递归使用模板
ArrayTP< ArrayTP<int,5> , 10 > twodee;
含义
twodee是包含10个元素的数组,其中每个元素都是一个包含
5个int元素的数组。
5个int元素的数组。
等价于int twodee[10][5]
使用
可直接用twodee[i][j]表示...
代码
代码
//录入数据
// twod.cpp -- making a 2-d array
#include <iostream>
#include "arraytp.h"
int main(void)
{
using std::cout;
using std::endl;
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";
// std::cin.get();
return 0;
}
结果
1 2 3 4 5 : sum = 15, average = 1.5
2 4 6 8 10 : sum = 30, average = 3
3 6 9 12 15 : sum = 45, average = 4.5
4 8 12 16 20 : sum = 60, average = 6
5 10 15 20 25 : sum = 75, average = 7.5
6 12 18 24 30 : sum = 90, average = 9
7 14 21 28 35 : sum = 105, average = 10.5
8 16 24 32 40 : sum = 120, average = 12
9 18 27 36 45 : sum = 135, average = 13.5
10 20 30 40 50 : sum = 150, average = 15
Done.
说明
使用多个类型参数
template<class T1 , class T2>
class pair{
....
};
class pair{
....
};
代码
// pairs.cpp -- defining and using a Pair template
#include <iostream>
#include <string>
template <class T1, class T2>
class Pair
{
private:
T1 a;
T2 b;
public:
T1 & first();
T2 & second();
T1 first() const { return a; }
T2 second() const { return b; } //这四个函数的区别注意,一个返回原本的同一个数,
//所以调用的函数可以作为左值修改返回的数。另一个返回临时对象
Pair(const T1 & aval, const T2 & bval) : a(aval), b(bval) { }
Pair() {}
};
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()
{
using std::cout;
using std::endl;
using std::string;
Pair<string, int> ratings[4] =
{
Pair<string, int>("The Purpled Duck", 5),
Pair<string, int>("Jaquie's Frisco Al Fresco", 4),
Pair<string, int>("Cafe Souffle", 5),
Pair<string, int>("Bertie's Eats", 3)
};
int joints = sizeof(ratings) / sizeof (Pair<string, int>); // 其实=4
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() = "Bertie's Fab Eats";
ratings[3].second() = 6;
cout << ratings[3].second() << ":\t "
<< ratings[3].first() << endl;
// std::cin.get();
return 0;
}
默认类型模板参数
template<class T1, clas T2 = int > class Topo{....};
Topo<doubledouble,double>m1;
Topo<double>m2;//省略T2值,编译器将使用int
Topo<doubledouble,double>m1;
Topo<double>m2;//省略T2值,编译器将使用int
说明
可以为类模板类型提供默认值,不能为函数模板参数提供默认值。
但都可以为非类型参数提供默认值。
但都可以为非类型参数提供默认值。
模板的具体化
隐式实例化
ArrayTP<int,100> stuff;
说明
根据所需类型,编译器使用通用模板所提供的处方生成具体的类定义
ArrayTP<double,30> *pt;// 不会生成类的隐式实例化
pt = new ArrayYP<double , 30>;//生成具体的类定义
pt = new ArrayYP<double , 30>;//生成具体的类定义
编译器在需要对象之前,不会生成类的隐式实例化
显式实例化
template class ArrayTP<string,100>;
说明
声明必须位于模板定义所在的名称空间中。(一般需要加名称空间,在哪里加?)
虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)
显式具体化
(整个模板类)
(整个模板类)
作用
特定类型(替换模板泛型)的定义
例如,模板
tempalate<typename T>
class SortedArray{
....
};
class SortedArray{
....
};
如果T是const char * 类型时,成员函数很多需要改动,需要重新定义一个关于
接受const char*类型的类。
接受const char*类型的类。
格式
template<>
class SortedArray<const char *>{
...
};
class SortedArray<const char *>{
...
};
说明
SortedArray<const char *> dates;//使用具体模板
当具体模板和通用模板都与实例化请求匹配,编译器使用具体化的版本
当具体模板和通用模板都与实例化请求匹配,编译器使用具体化的版本
早期的编译器格式为
class SortedArray<const char *>
//没有template<>
class SortedArray<const char *>
//没有template<>
(模板类中的成员函数具体化)
好像没有?那需要怎么办?只是某个函数不一样,其他不需要改怎么办?
部分具体化
1
作用
类型参数之1指定具体类型
例如,模板
template<class T1,chass T2>
class Pair{
...
};
class Pair{
...
};
格式
template<class T1>
class Pair<T1,int>{
....
}
//部分实例化
class Pair<T1,int>{
....
}
//部分实例化
template<>
class Pair<int ,int>{
...
};
class Pair<int ,int>{
...
};
说明
多个模板可选择时,编译器将使用具体化程度最高的模板
Pair<double,double>p1;//use general Pair template;
Pair<double,int>//use Pair<T1,int>;
Pair<int,int>//use Pair<int,int>;
Pair<double,double>p1;//use general Pair template;
Pair<double,int>//use Pair<T1,int>;
Pair<int,int>//use Pair<int,int>;
2
作用
为指针提供特殊版本来部分具体化现有模板
格式
template<class T>
class Feeb{ .... };
template<class T*>
class Feeb{ ... };
class Feeb{ .... };
template<class T*>
class Feeb{ ... };
说明
Feeb<char> fb1;
Feeb<char *> fb2; // use Feeb T*;
Feeb<char *> fb2; // use Feeb T*;
成员模板
内容
1.模板类将另一个模板类作为其成员
2.模板类将模板函数作为其成员
代码
template <typename T>
class beta
{
private:
template <typename V> // nested template class member
class hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
hold<T> q; // template object
hold<int> n; // template object
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // template method
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
void Show() const { q.show(); n.show();}
};
int main()
{
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";
cout << "Done\n";
// std::cin.get();
return 0;
}
结果
T was set to double
3.5
3
V was set to T, which is double, then V was set to int
28
U was set to int
28.2609
U was set to double
Done
Press any key to continue
说明
1.
在类内声明hold类方法
格式
上代码
说明
deta类中,可以直接用hold类中的公有接口,私有接口不可用。
在类外声明hold类方法
格式
deta中
template<typename T>
class deta{
...
private:
template<typename V>
class hold;
.....
}
class deta{
...
private:
template<typename V>
class hold;
.....
}
类外声明
template<typename T>
template<typename V>
class beta<T>::hold
{.....};
template<typename V>
class beta<T>::hold
{.....};
那hold的成员函数也在外定义的话怎么办?
template<typename T>
template<typename V>
void beta<T>::hold<V>::show() const
{.....}//???
template<typename V>
void beta<T>::hold<V>::show() const
{.....}//???
说明
有些编译器不支持类外面定义
hold是类的成员,所以要通过使用作用域解析运算符来完成
2.
在类外定义Ublab方法
格式
deta中
template<ctypename T>
class deta{
...
public:
template<template U>
U blab( U u , T t);
};
class deta{
...
public:
template<template U>
U blab( U u , T t);
};
类外定义
template<template T>
template<template U>
U beta<T>::blab(U u, T t)
{
......
}
template<template U>
U beta<T>::blab(U u, T t)
{
......
}
说明
blab中,第二个参数由T决定,第一个参数U根据其具体情况决定
eg:
cout << guy.blab(10, 2.3) << endl; U被设置为int;
cout<<guy.blab(10.0 , 2.3)<<endl; U被设置为诶double;
eg:
cout << guy.blab(10, 2.3) << endl; U被设置为int;
cout<<guy.blab(10.0 , 2.3)<<endl; U被设置为诶double;
如果
guy.blab( 10, 3 ) //会根据函数原型规则把3转换为double 类型
guy.blab( 10, 3 ) //会根据函数原型规则把3转换为double 类型
模板用作参数
代码
// tempparm.cpp ?templates as parameters
#include <iostream>
#include "stacktp.h"
template <template <typename T> class Thing>
class Crab
{
private:
Thing<int> s1;
Thing<double> s2;
public:
Crab() {};
// assumes the thing class has push() and pop() members
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); }
};
int main()
{
using std::cout;
using std::cin;
using std::endl;
Crab<Stack> nebula;
// Stack must match template <typename T> class thing
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";
// cin.get();
// cin.get();
return 0;
}
结果
Enter int double pairs, such as 4 3.5 (0 0 to end):
50 22.48
25 33.87
60 19.12
0 0//(自己输入的)
60, 19.12
25, 33.87
50, 22.48
Done.
作用
(应用)
一般用于实现STL
(应用)
一般用于实现STL
在Crab中
Thing<int>s1;
Thing<double>s2;
为其私有数据成员。
Thing<int>s1;
Thing<double>s2;
为其私有数据成员。
格式
类声明
template<template<typename T> class Thing>
class Crab{
private:
Thing<int>s1;
Thing<double>s2;
.....
};
class Crab{
private:
Thing<int>s1;
Thing<double>s2;
.....
};
说明
template<typename T> 是类型,Thing是参数
//
template<typename T>——typename/class
Thing ——Type/T
//
template<typename T>——typename/class
Thing ——Type/T
main中,Crab<Stack> nebula;用Stack<int> s1,代替Thing <int> s1
main中,若有Crab<king> legs;
则king一定要是类模板;与Thing匹配,即King要有以下定义
template<typename T>
class King{ ... };
则king一定要是类模板;与Thing匹配,即King要有以下定义
template<typename T>
class King{ ... };
模板类和友元
非模板友元
含义
函数本身不是模板,有时会使用一个模板参数(或者不使用)
情形
1
格式
//类声明
template<typename T>
class HasFirend{
public:
friend void counts()
......
}
template<typename T>
class HasFirend{
public:
friend void counts()
......
}
//类定义
void counts()
{
....
};
void counts()
{
....
};
问题
count对象不通过对象调用,没有对象
参数,如何访问Hasfirend对象?
参数,如何访问Hasfirend对象?
- 可以访问全局对象
??体现
2. 可以使用全局指针访问非全局对象
??体现
3. 可以创建自己的对象
??体现
4. 可以访问独立于对象的模板类的静态数据成员(应用用的是这种)
第4点的应用
代码
//类声明中
...
class HasFriend{
......
static int ct;
.......
friend void counts();
.....
};
...
class HasFriend{
......
static int ct;
.......
friend void counts();
.....
};
//类定义中
void couts()
{
cout<<“Int count”<<HasFriend<int>::ct<<";";
cout<<"double count"<<HasFriend<double>::ct<<";";
void couts()
{
cout<<“Int count”<<HasFriend<int>::ct<<";";
cout<<"double count"<<HasFriend<double>::ct<<";";
说明
ct为静态数据成员,意味类的每一个特定的具体化都有自己的静态成员。
cout()是所用HasFriend具体化得友元,每个HasFriend<>对象都可以调用count,且内容一样。
2
格式
//类声明
template<typename T>
class HasFriend{
.....
public:
firend void reports(HasFriend<T>&);//template parameter模板参数
........
}
template<typename T>
class HasFriend{
.....
public:
firend void reports(HasFriend<T>&);//template parameter模板参数
........
}
//类定义(与类声明在同一个头文件中)
....
void reports( HasFriend<double> & hf )
{
.......
}
void reports(HasFriend<int> & hf){
...
}
....
void reports( HasFriend<double> & hf )
{
.......
}
void reports(HasFriend<int> & hf){
...
}
说明
这里的友元函数虽然用到了模板参数,但是本身不是模板
类声明中/类定义中,没有template<>打头
类声明中/类定义中,没有template<>打头
在类定义中根据不同情形要具体化。(虽然只有一个函数原型)
void reports(HasFriend<T> & hf ) 类定义中,直接这样是不可以的
void reports(HasFriend<T> & hf ) 类定义中,直接这样是不可以的
void reports( HasFriend & hf ) 是HasFriend<double>的友元
void reports(HasFriend<int> & hf)是HasFriend<int>的友元
void reports(HasFriend<int> & hf)是HasFriend<int>的友元
应用代码
// frnd2tmp.cpp -- template class with non-template friends
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class HasFriend
{
private:
T item;
static int ct;
public:
HasFriend(const T & i) : item(i) {ct++;}
~HasFriend() {ct--; }
friend void counts();
friend void reports(HasFriend<T> &); // template parameter
};
// each specialization has its own static data member
template <typename T>
int HasFriend<T>::ct = 0;
// non-template friend to all HasFriend<T> classes
void counts()
{
cout << "int count: " << HasFriend<int>::ct << "; ";
cout << "double count: " << HasFriend<double>::ct << endl;
}
// non-template friend to the HasFriend<int> class
void reports(HasFriend<int> & hf)
{
cout <<"HasFriend<int>: " << hf.item << endl;
}
// non-template friend to the HasFriend<double> class
void reports(HasFriend<double> & hf)
{
cout <<"HasFriend<double>: " << hf.item << endl;
}
int main()
{
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);
// std::cin.get();
return 0;
}
模板类的
约束友元
约束友元
含义
使友元函数本身成为模板,使类的每一个具体化都获得与友元匹配的具体化
步骤
在类定义的前面
声明每个模板函数
声明每个模板函数
template<typename T> void counts();
template<typename T> void report( T & hf );
template<typename T> void report( T & hf );
说明
具体位置见下代码
在函数中再次将
模板声明声明友元
模板声明声明友元
template<typename TT>
class HasFriendT
{
....
friend void counts<TT>( );
friend void report<>(HasFriendT<TT> & );
class HasFriendT
{
....
friend void counts<TT>( );
friend void report<>(HasFriendT<TT> & );
说明
- count没有参数,必须使用模板参数语法(<TT>)来指明其具体化。
- report的<>为空,因为可以从函数参数推断出模板类型参数。
- 当然,也可以使用:
为友元提供模板定义
template<typename T>
void counts(){
....
}
void counts(){
....
}
template<typename T>
void report( T & hf ){
......
}
void report( T & hf ){
......
}
应用代码
// tmp2tmp.cpp -- template friends to a template class
#include <iostream>
using std::cout;
using std::endl;
// template prototypes
template <typename T> void counts();
template <typename T> void report(T &);
// template class
template <typename TT>
class HasFriendT
{
private:
TT item;
static int ct;
public:
HasFriendT(const TT & i) : item(i) {ct++;}
~HasFriendT() { ct--; }
friend void counts<TT>();
friend void report<>(HasFriendT<TT> &);
};
template <typename T>
int HasFriendT<T>::ct = 0;
// template friend functions definitions
template <typename T>
void counts()
{
cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
cout << "template counts(): " << HasFriendT<T>::ct << endl;
}
template <typename T>
void report(T & hf)
{
cout << hf.item << endl;
}
int main()
{
counts<int>();
HasFriendT<int> hfi1(10);
HasFriendT<int> hfi2(20);
HasFriendT<double> hfdb(10.5);
report(hfi1); // generate report(HasFriendT<int> &)
report(hfi2); // generate report(HasFriendT<int> &)
report(hfdb); // generate report(HasFriendT<double> &)
cout << "counts<int>() output:\n";
counts<int>();
cout << "counts<double>() output:\n";
counts<double>();
// std::cin.get();
return 0;
}
说明
每种T类型都有自己的友元函数counts( );
模板类的
非约束友元
非约束友元
含义
每个(友元)函数具体化都是每个类具体化的友元。
特点
约束模板友元函数——在类外声明的模板具体化。
非约束模板友元函数——在类内部声明模板。
非约束模板友元函数——在类内部声明模板。
非约束友元模板类型参数与模板类类型参数不同
格式
//类声明
template<typename T>
class ManyFriend
{
....
template<typename C , typename D>friend void show2( C& , D&);
}
template<typename T>
class ManyFriend
{
....
template<typename C , typename D>friend void show2( C& , D&);
}
//类定义
template<typename C,typename D>
void show2( C& c , D& d)
{
.........
}
template<typename C,typename D>
void show2( C& c , D& d)
{
.........
}
应用代码
// manyfrnd.cpp -- unbound template friend to a template class
#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class ManyFriend
{
private:
T item;
public:
ManyFriend(const T & i) : item(i) {}
template <typename C, typename D> friend void show2(C &, D &);
};
template <typename C, typename D> void show2(C & c, D & d)
{
cout << c.item << ", " << d.item << endl;
}
int main()
{
ManyFriend<int> hfi1(10);
ManyFriend<int> hfi2(20);
ManyFriend<double> hfdb(10.5);
cout << "hfi1, hfi2: ";
show2(hfi1, hfi2);
cout << "hfdb, hfi2: ";
show2(hfdb, hfi2);
// std::cin.get();
return 0;
}
模板别名
1.
typedef std::array<double , 12> arrd
arrd gallons; // 相当于std::array<double,12> gallons;
2.(C++11)
template<typename T>
using arrtype= std::array<T, 12>;
using arrtype= std::array<T, 12>;
arrtype<double> gollons; //相当于std::array<double,12>;
0 条评论
下一页