第十六章 继承与多重继承

第十六章 继承

16.1 继承的概念:

  面向对象程序设计可以让你声明一个新类作为另一个类的派生。派生类(又称子类)继承父亲的属性和操作。子类也声明了新的属性和新的操作,剔除了那些不适合于其用途的继承下来的操作。即,继承可以让你重用父类的代码,专注于为子类编写新代码,继承可以使已经存在的类不需改动的适应新应用。

16.2 继承的工作方式:

  例如,现在有一个学生类student,要新建一个研究生类graduatestudent。研究生除了具有研究生的性质还具有学生的所有性质,所以我们用继承的方式重用学生类。

class student
{
    //...
};
class graduatestudent:public student
{
    //...
};

  graduatestudent类继承了student类的所有成员。继承的方式如上。而graduatestudent类也有自己特定的成员。
举一个例子:继承student类

#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
class Advisor//导师资料
{
    int noofmeeting;
};
class student
{
public:
    student(char* pname = "no name")
    {
        strcpy_s(name, pname);
        average = semesterhours = 0;
    }
    void addcourse(int hour, float grade)
    {
        average = (semesterhours * average + grade);//总分
        semesterhours += hour;//总修学时
        average /= semesterhours;//平均分
    }
    int gethours()
    {
        return semesterhours;
    }
    float getaverage()
    {
        return average;
    }
    void display()
    {
        cout << "name=\"" << name << "\""
            << ",hour=" << semesterhours
            << ",average=" << average << endl;
    }
protected:
    char name[60];
    int semesterhours;
    float average;
};
class graduatestudent :public student
{
public:
    int getqualifier()
    {
        return qualifiergrade;
    }
protected:
    Advisor advisor;
    int qualifiergrade;
};
int main()
{
    student ds("lo lee undergrade");
    graduatestudent gs;
    ds.addcourse(3, 2.5);
    ds.display();
    gs.addcourse(3, 3.0);
    gs.display();
    return 0;
}

  ds是student类对象,gs是graduatestudent类对象。作为student的子类,对象gs可以做ds能做的任何事,它有name,semesterhour average数据成员,以及addcourse()成员函数,此外gs比ds还多一点东西,它有导师Advisor和资格考试分qualifiergrade。
  gs是一个学生,所以对student中的addcourse()成员函数的调用等于是在调用自己的成员函数。

void fn(student*s)
{
    //任何学生想要干的事
}
int main() {
    graduatestudent gs;
    fn(gs);
}

  虽然fn()函数期望接受的是student类对象,但是来自main()的调用传给他一个graduatestudent对象,fn()把它视同为student对象并接受。

16.3 派生类的构造:

  在上述代码中,没有声明派生类graduatestudent的构造函数,导致派生类对象创建时,执行默认构造函数,该默认构造函数会首先调用基类的默认构造函数,而基类没有默认构造函数,但正好匹配默认参数的构造函数。导致gs对象的name为"no name"。

16.4 继承与组合:

  类以另一个类对象作数据成员,称为组合。在程序中,graduatestudent类组合了Advisor类。这种场合,称graduatestudent有一个Advisor;而在继承的组合,称graduatestudent是一个student。
  继承和组合的区别:

#include<iostream>
using namespace std;
class vehicle
{
//...
};
class Motor//马达
{
//...
};
class car :public vehicle
{
public:
    Motor motor;
};
void vehiclefn(vehicle &v);
void motorfn(Motor &m);
int main()
{
    car c;
    vehiclefn(c);
    motorfn(c);//error.参数要求是马达,而汽车不是马达,所以参数c不匹配该函数。
    motorfn(c.motor);
    return 0;
}
16.5 多态性:

  在16.2代码中,graduatestudent类对象gs调用student类的成员函数display(),该函数在输出时,没办法输出graduatestudent自己的数据成员qualifierfrade。因此继承时希望重载display()。
  在下面代码中,基类和派生类中都定义了计算学费的函数:

#include<iostream>
using namespace std;
class student
{
public:
    //...
    float calctuition()//计算学费
    {
        //...
    }
};
class graduatestudent:public student
{
public:
    //...
    float calctuition()
    {
        //...
    }
};
int main()
{
    student s;
    graduatestudent gs;
    s.calctuition;
    gs.calctuition;
    return 0;
}



//
class student
{
public:
    //...
    float calctuition()//计算学费
    {
        //...
    }
};
class graduatestudent:public student
{
public:
    //...
    float calctuition()
    {
        //...
    }
};
void fn(student&x)
{
    x.calctuition();
}
int main()
{
    student s;
    graduatestudent gs;
    fn(s);
    fn(gs);
    return 0;
}

17.多重继承

17.1 多重继承如何工作:
#include<iostream>
using namespace std;
class Bed
{
public:
    Bed():weight(0){}
    void sleep()
    {
        cout<<"sleeping...\n";
    }
    void setweight(int i)
    {
        weight=i;
    }
protected:
    int weight;
};
class Sofa
{
public:
    Sofa():weight(0){}
    void watchtv()
    {
        cout<<"watchtv...\n";
    }
    void setweight(int i){weight=i;}
protected:
    int weight;
};
class sleepersofa:public Bed,public Sofa
{
public:
    sleepersofa(){}
    void foldout()//折叠与打开
    {
        cout<<"fold out the sofa.\n";
    }
};
int main()
{
    sleepersofa ss;//只需定义一个对象便可调用三个类
    ss.watchtv();
    ss.foldout();
    ss.sleep();
    return 0;
}
17.2 继承的模糊性:

在上节中,Sofa 和 Bed 都有一个成员weight。但是sleepersofa继承哪个weight呢?既然两者都继承,因为名字一样,使得对weight引用变得模糊不清。如何引用呢?

int main()
{
    sleepersofa ss;
    ss.setweight(20);//Bed的setweight还是Sofa的setweight?
    return 0;
}
//导致名字冲突,编译时错误
int main()
{
    sleepersofa ss;
    ss.sofa.setweight(20);//说明sofa的重量是20
    return 0;
}
17.3 虚拟继承
  public 前面加上 virtual

  我们可以通过分解具体考察他们的关系:

#include<iostream>
using namespace std;
class Furniture//家具
{
public:
    Furniture(){}
    void setweight(int i){weight=i;}
    int getweight(){return weight;}
protected:
    int weight;
};
class Bed:public Furniture
{
public:
    Bed(){}
    void sleep()
    {
        cout<<"sleeping...\n";
    }
};
class Sofa:public Furniture
{
public:
    Sofa(){}
    void watchtv()
    {
        cout<<"watching TV.\n";
    }
};
class sleepersofa:public Bed,public Sofa
{
public:
    sleepersofa():Sofa(),Bed(){}
    void foldout()
    {
        cout<<"fold out the sofa.\n";
    }
};
int main()
{
    sleepersofa ss;
    ss.setweight(20);//error. 模糊的setweight成员
    Furniture *pf;
    pf=(Furniture*)&ss;//error.模糊的Furniture成员
    cout<<pf->getweight()<<endl;
}

  这里的sleepersofa不是直接继承Furniture,而是Bed和Sofa各自继承Furniture。这里sleepersofa包括一个完整的Bed和完整的Sofa,后面还有一个sleepersofa特有的东西,而sleepersofa中的每一个子对象都有它自己的Furniture部分,导致了继承层次的错误。不知道setweight()属于哪一个Furniture成员,指向Furniture的指针也不知道是指向哪一个Furniture导致编译错误。
  sleepersofa只需要一个Furniture,所以我们希望它只含一个Furniture拷贝,同时又要共享Bed和Sofa的成员函数与数据成员。这时候就需要虚拟继承:

#include<iostream>
using namespace std;
class Furniture//家具
{
public:
    Furniture(){}
    void setweight(int i){weight=i;}
    int getweight(){return weight;}
protected:
    int weight;
};
class Bed:virtual public Furniture
{
public:
    Bed(){}
    void sleep()
    {
        cout<<"sleeping...\n";
    }
};
class Sofa:virtual public Furniture
{
public:
    Sofa(){}
    void watchtv()
    {
        cout<<"watching TV.\n";
    }
};
class sleepersofa:public Bed,public Sofa
{
public:
    sleepersofa():Sofa(),Bed(){}
    void foldout()
    {
        cout<<"fold out the sofa.\n";
    }
};
int main()
{
    sleepersofa ss;
    ss.setweight(20);//error.
    cout<<ss.getweight()<<endl;
}

虚拟继承的虚拟和虚拟函数的虚拟没有关系。

17.4 多继承的构造顺序:

(1). 任何虚拟基类的构造函数按照它们被继承的顺序构造。
(2). 任何非虚拟基类的构造函数按照它们被继承的顺序构造。
(3). 任何成员对象的构造函数按照它们声明的顺序被调用。
(4). 类自己的构造函数。

17.5 继承的访问控制:

Ps:本文是学习笔记,内容来自清华大学出版社出版的C++程序设计教程(修订版)——设计思想与实现。钱能著。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JdiLfc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值