第十四章 c++代码重用

私有继承

私有继承实现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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值