私有继承
私有继承实现has-a的关系,基类的公有成员和保护成员都将称为派生类的私有成员,基类方法将不会称为派生对象公有的接口的一部分,但可以在派生类的成员函数中使用。
私有继承提供的特性和包含相同,但捕获得接口。所以私有继承也可以是用来实现has-a关系。
包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个未被命名的继承对象添加到类中。
访问基类对象:
私有继承方位基类对象,可以使用强制类型转换,将派生类转换未基类指针或引用,来访问基类对象。
私有继承中,在不进行显式类型转换的情况下,不能将指向派生类的引用或指针赋给基类引用或指针。
使用包含还是私有继承
通常,应使用包含来建立has-a关系,如果新类需要访问原有类的保护成员,需要重新定义虚函数,则应使用私有继承。
保护继承
class Student :protected string,protected valarray<double>
{}
保护继承基类的公有成员和保护成员都将成为派生类的保护成员。基类的接口在派生类中也是可用的,但在继承层次结构之外是不可用的。当从派生类派生出另一个类时,私有继承的第三代将不能使用基类的接口,因为基类的方法在派生类变成了私有的,使用保护继承时,基类的公有方法在第二代中将变成保护成员,第三代派生类可以使用。
使用using重新定义访问权限
使用保护派生或者私有派生时,基类的公有成员将变成保护或者私有的成员。如果要让基类的方法在派生类之外可用,有两种方法。
1 定义一个使用该基类方法的派生类公有方法。
double Student::sum() const {
return std::valarray<double>::sum();
}
2 使用using声明,指出派生类可以使用特定的基类成员。
class Student :protected string,protected valarray<double> {
public:
using std::valarray<double>::min;
using std::valarray<double>::max;
}
using 使得min和max可用,就像使用公有方法一样。
多重继承
class SingingWaiter :public Waiter,public Singer {}
多重继承会引入一些问题,例如Waiter和Singer都是继承自Worker
因为Singer和Waiter都是继承自Workor,所以SingingWaiter中将会包含两个Workor
SingingWaiter ed;
Worker *pw = &ed;
这样调用程序将有二义性。
针对上述问题,C++引入了虚基类
class Singer :virtual public Workor {}
class Waiter :virtual public Workor {}
这样SingingWaiter对象将只包含一个Workor对象的副本
注意虚基类和虚函数没有任何关系,只是起的名字而已
多重继承引入问题
1 SingingWaiter 调用Show方法,但是自己没有Show方法,对于单继承,将使用最近祖先的定义,但是多重继承两个父类都有此方法,将存在二义性。
SingingWaiter newhire("Elise Hawks", 2005, 6, soprano);
newhire.Show();
C++使用作用域解析附解决此问题
SingingWaiter newhire("Elise Hawks", 2005, 6, soprano);
newhire.Singer::Show();
更好的方法时重新定义Show(),并指出使用的是哪个show
void SingingWaiter::Show() {
Singer::Show();
}
类模板
模板类以template <class Type> 开头,或者 template <typename Type>
模板并不是类和成员函数定义,是C++的编译指令,在编译阶段生效,用来说明如何生成类和成员函数定义。模板的成员函数和声明,应该放到一个头文件中,并在要使用这些模板的文件中包含该头文件。模板不是函数不能单独编译。
template <class Type>
class Stack {
private:
int top;
public:
Stack();
bool isempty();
bool push(const Type & item);
};
template <class Type>
Stack<Type>::Stack() {
top = 0;
}
template <class Type>
bool Stack<Type>::push(const Type & item) {
if (top < MAX) {
item[top++] = item;
return true;
} else {
return false;
}
}
使用模板类
实例化模板,通过模板创建实例对象。
Stack<int> kernels;
Stack<string> coloels;
数组模板和非类型参数
template <class T, int n> //n为非类型参数
class ArrayTP {
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & V);
vitural T & operator[](int i);
vitural T operatot[](int i) const;
}
声明模板对象方法:
ArrayTP<double, 12> eggweights;
编译器将定义名为ArrayTP<double, 12>的类,并创建一个类型为ArrayTP<double, 12> 的eggweights对象。定义类时编译器将使用double替换T,12替换n
模板具体化
1 隐式实例化
ArrayTP<int, 100> stuff;声明一个或多个对象,指出所需的类型。
ArrayTP<int, 100> *pt;
pt = new ArrayTP<int, 100>;
第二条语句导致编译器生成类的定义,并根据该定义创建一个对象。
2 显式实例化
使用关键字template,并指出所需类型来声明类时,编译器将生成类声明的显示实例化。
template class ArrayTP<int, 100>;
这种虽然没有实例化类,但是会生成类声明,包括方法定义。
3 显示具体化
将模板用作参数
template <template <typename T> class Thing>
class Crab
Crab<King> legs;
模板参数King必须是一个模板类,其声明与模板参数Ting的声明匹配
模板别名
可以使用typedef为模板指定别名
typedef std::array<double, 12> arrd; //arrd为模板具体化别名
arrd gallons;实例化gallons对象
使用using
template<typename T>
using arrtype = std::<T, 12>;
将arrtype定义为一个模板别名,可使用它来指定类型。
arrtype<double> gallons;
aeetype<int> days;