C++基本语法3--C++程序设计教程/钱能主编--清华大学出版社

第十六章   继承

virtual 作用:多态性、抽象类、虚拟继承

16.2 继承的工作方式:

class Student

{

/

};

class GraduateStudent : public Student

{

///

};

16.4 继承与组合

类以另一个类对象做数据成员,称为组合。

16.5 多态性

C++允许子类的成员函数重载基类的成员函数。

class Student

{

float caltuition();  //计算学费

};

class GraduateStudent : public Student

{

float caltuition();   //对基类的该函数进行重载

};

void fn(Student& x)

{

x.caltuition();   // 到底该调用学生的还是研究生的???

}

void main()

{

Student s;

GraduateStudent  gs;

fn(s);   //计算学生s的学费

fn(gs);  //  计算研究生gs的学费

}

C++的继承机制中用一种称为多态性的技术来解决上面的问题。这种在运行时,能依据其类型确认调用哪个函数的能力,称为多态性,或称迟后联编,也有的称为滞后联编。

编译时就能确定哪个重载函数被调用的,称为先期联编(Early binding)。

为了指明某个成员函数具有多态性,用关键字virtual来标志其为虚函数。

16.8 不恰当的虚函数

如果虚函数在基类与子类中出现的仅仅是名字的相同,而参数类型不同,或返回类型不同,即使写上了virtual关键字,则也不进行迟后联编。  有一种例外,如果基类中的虚函数返回一个基类指针或返回一个基类的引用,子类中的虚函数返回一个子类的指针或子类的引用,则c++将其室外同名虚函数而进行迟后联编。

在函数声明的参数里的&是引用,函数调用的参数里的&是取地址

16.9 虚函数的限制

一个类中将所有的成员函数都尽可能的设置为虚函数总是有益的。设置虚函数,须要注意一下几点:

1)只有类的成员函数才能说明为虚函数。

2)静态成员函数不能是虚函数,因为静态成员函数不受限于某个对象。

3)内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的。

4)构造函数不能是虚函数

5)析构函数可以是虚函数。

16.13 c++允许声明一个不能有实例对象的类,遮掩的类的唯一的作用是被继承。这种类称为抽象类。

一个抽象类至少具有一个纯虚函数。所谓纯虚函数是指被标明为不具体实现的虚成员函数。

virtual returnType func(type,type,。。。)= 0 ; //纯虚函数

声明之后的 “ =0  ” 表明程序员将不定义该函数,由此类的一个派生类来重载。

16.15 不能创建一个抽象类的对象,但是可以声明一个抽象类的指针或引用。

纯虚函数是在基类中为子类保留的一个位置,以便子类用自己的实在函数定义来重载之。如果在基类中没有保留位置,则就没有重载。

第十七章  多重继承

17.3 虚拟继承

虽然说虚拟继承与虚函数有一定相似的地方,但读者务必要记住,他们之间是绝对没有任何联系的!

在前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会自动先加入一个Vehicle的拷贝,当再次请求一个Vehicle的拷贝的时候就会被忽略,保证继承类成员函数的唯一性。

先看如下的图示:

在图中深红色标记出来的地方正是主要问题所在,水陆两用汽车类继承了来自Car类与Boat类的属性与方法,Car类与Boat类同为AmphibianCar类的基类,在内存分配上AmphibianCar获得了来自两个类的SetWeight()成员函数,当我们调用a.SetWeight(3)的时候计算机不知道如何选择分别属于两个基类的被重复拥有了的类成员函数SetWeight()。

由于这种模糊问题的存在同样也导致了AmphibianCar a(4,200,1.35f);执行失败,系统会产生Vehicle”不是基或成员的错误。

以上面的代码为例,我们要想让AmphibianCar类既获得一个Vehicle的拷贝,而且又同时共享用Car类与Boat类的数据成员与成员函数就必须通过C++所提供的虚拟继承技术来实现。

我们在Car类和Boat类继承Vehicle类出,在前面加上virtual关键字就可以实现虚拟继承,使用虚拟继承后,当系统碰到多重继承的时候就会自动先加入一个Vehicle的拷贝,当再次请求一个Vehicle的拷贝的时候就会被忽略,保证继承类成员函数的唯一性

修改后的代码如下,注意观察变化:

//程序作者:管宁  
//站点:www.cndev-lab.com  
//所有稿件均有版权,如要转载,请务必著名出处和作者  
 
#include <iostream
using namespace std; 
 
class Vehicle 

    public
        Vehicle(int weight = 0) 
        { 
            Vehicle::weight = weight; 
            cout<<"载入Vehicle类构造函数"<<endl; 
        } 
        void SetWeight(int weight) 
        { 
            cout<<"重新设置重量"<<endl; 
            Vehicle::weight = weight; 
        } 
        virtual void ShowMe() = 0; 
    protected
        int weight; 
}; 
class Car:virtual public Vehicle//汽车,这里是虚拟继承

    public
        Car(int weight=0,int aird=0):Vehicle(weight) 
        { 
            Car::aird = aird; 
            cout<<"载入Car类构造函数"<<endl; 
        } 
        void ShowMe() 
        { 
            cout<<"我是汽车!"<<endl; 
        } 
    protected
        int aird; 
}; 
 
class Boat:virtual public Vehicle//船,这里是虚拟继承

    public
        Boat(int weight=0,float tonnage=0):Vehicle(weight) 
        { 
            Boat::tonnage = tonnage; 
            cout<<"载入Boat类构造函数"<<endl; 
        } 
        void ShowMe() 
        { 
            cout<<"我是船!"<<endl; 
        } 
    protected
        float tonnage; 
}; 
 
class AmphibianCar:public Car,public Boat//水陆两用汽车,多重继承的体现

    public
        AmphibianCar(int weight,int aird,float tonnage) 
        :Vehicle(weight),Car(weight,aird),Boat(weight,tonnage) 
        //多重继承要注意调用基类构造函数
        { 
            cout<<"载入AmphibianCar类构造函数"<<endl; 
        } 
        void ShowMe() 
        { 
            cout<<"我是水陆两用汽车!"<<endl; 
        } 
        void ShowMembers() 
        { 
            cout<<"重量:"<<weight<<"顿,"<<"空气排量:"<<aird<<"CC,"<<"排水量:"<<tonnage<<"顿"<<endl; 
        } 
}; 
int main() 

    AmphibianCar a(4,200,1.35f); 
    a.ShowMe(); 
    a.ShowMembers(); 
    a.SetWeight(3); 
    a.ShowMembers(); 
    system("pause");  
}

注意观察类构造函数的构造顺序。

虽然说虚拟继承与虚函数有一定相似的地方,但读者务必要记住,他们之间是绝对没有任何联系的!

17.4 多继承的构造顺序

构造对象的规则需要扩展以控制多重继承。构造函数按下列顺序被调用:

1)任何虚拟基类的构造函数按照他们被继承的顺序构造。

2)任何非虚拟基类的构造函数按照他们被继承的顺序构造。

3)任何成员对象的构造函数按照他们声明的顺序调用。

4)类自己的构造函数。

17.5 

继承和访问控制

基本规则

  在基类中,public和private标号具有普通意义:用户代码可以访问类的public成员而不能访问private成员,private成员只能由基类的成员和友元访问。派生类对基类的public和private成员的访问权限与程序中任意其他部分一样:它可以访问pubic成员而不能访问private成员。

  有时作为基类的类具有一些成员,他希望允许派生类访问但继续禁止其他用户访问这些成员,对于这样的成员应该使用受保护的访问标号(protected),protected成员可以被派生类对象访问但不能被该类型的普通用户访问。

protected成员

  可以认为protected访问标号是private和public的混合:

    1. 像private成员一样,protected成员不能被类的用户访问

    2. 像public成员一样,protected成员可被该类的派生类访问

  此外,protected还有另一个重要的性质:

    1. 派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限

公有,私有和受保护的继承

  基类本身指定对自身成员的最小访问控制.如果成员在基类中为private,则只有基类和基类的友元可以访问该成员.派生类不能访问基类的private成员,也不能使自己的用户能够访问那些成员.如果基类成员为public或者protected,则派生列表中使用的访问标号决定该成员在派生类中的访问级别:

    1. 如果是公用继承,基类成员保持自己的访问级别:基类的public成员为派生类的public成员,基类的protected成员为派生类的protected成员.(基类中的private 成员在子类中不能访问

    2. 如果是受保护的继承,基类的public和protected成员在派生类中为protected成员。(基类中的private成员在子类中不能访问

    3. 如果是私有继承,基类的public和protected成员在派生类中为private成员.(基类中的private成员在子类中不能访问


在公共继承的类中,基类的每个成员在子类中保持同样的访问访问方式。即在基类中为public的成员,子类中可以访问之,并据为public的;基类中为protected的成员,子类中叶可访问之,并据为protected;基类中为private的成员,在子类中不能访问之,这就像在应用程序中不能访问类中私有成员一样。

在继承关系中,基类的private成员不但对应于程序隐藏,甚至对派生类也隐藏。而基类的保护成员则只对应用程序隐藏,而对派生类则豪不隐藏。

一个私有的或保护的派生类不是子类。因为非公共的派生类不能做基类能做的所有的事。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值