2.1 C++面向对象编程_访问控制和继承

面向对象的编程有三大特点:

  1. 封装;
    1. 抽象出数据成员、成员函数;
    2. 访问控制;
  2. 继承;
  3. 多态;

本节来学习继承。

继承

之前定义过一个Person类如下:

class Person {
private:
    static int cnt;
    char *name;
    int age;

public:
    ...
};

如果现在要再定义一个Student类,Student也是Person,难道之前在Person类中定义过的成员在Student类中都要再定义一次吗?

答:不需要,可以使用继承。Student类继承Person类,这样Student类就会有Person类的所有属性。

Student类定义如下:

class Student : public Person {
};

在Student类中,我们没有额外定义成员,在main函数中,测试一下Student类是否可以使用Person类的成员。

测试结果如下:

可以看到,Student类使用了Person类的成员,也就是说,Student类成功的继承了Person类的成员定义。

继承的控制关系

为了讲述继承的控制关系,我们重新创建了一个father_son.cpp。

在father_son.cpp中,我们重新创建了一个Father类,然后创建了一个继承Father类的Son类,代码如下:

 

然后写出main函数,先测试一下继承是否有问题。

测试结果如下,可以看到,继承成功了。

接下来需要测试一下继承的权限,主要有以下几点:

  1. Son不能直接拿Father的私房钱:派生类不能访问基类的私有成员;
  2. Son可以向Father要钱:通过protected/public的成员函数;
  3. 儿子总是比外人亲:派生类可以访问protected成员,其他代码不可以;

依次测试。

1、派生类不能访问基类的私有成员

在派生类中直接操作基类的Private成员,代码如下:

编译报错,money是Father类的私有成员,派生类Son无法直接访问。

 2、通过protected/public的成员函数

派生类无法直接访问基类的私有成员,但是可以通过基类的protected/public成员函数间接访问基类的私有成员。

测试结果如下:

3、 派生类可以访问protected成员,其他代码不可以

如果将get_money和set_money都定义为public类型,那么Father的钱就很难保护了,但是设置为Private类的话,Son又获取不了了。

这时候,可以将它们设置为protected类型,这样派生类Son可以访问,但是其他代码就不可以了。

这时候,在main函数中调用set_money和get_money就会报错。

 

但是,在派生类Son中调用仍然是正常的。

将main函数中set_money和get_money的调用删掉。

在Son类中调用。

 

 编译代码就没有问题了,测试结果也符合预期。

类的成员类型

总结一下类的成员类型:

  1. private:外界不可见,不能直接访问;
  2. protected:外接不可见,不能直接访问;子类可以访问;
  3. public:外界可以直接访问;

调整访问控制

有时候,基类的成员类型并不满足派生类的需要,那么可能就需要修改基类的成员类型。

在Father类中添加一个protected类型的成员room_key,此时在派生类son中可以自由使用room_key,但是在别的函数体中不能自由使用。

那么,是否有办法让其他函数体也能自由使用呢?

答:有,在Son类中将room_key成员变为public类型就可以了。

代码如下,在public类型中使用using Father::room_key,此时再编译就没有报错了。

 

既然可以将room_key设置为public,那么是否可以将room_key设置为private?

调整代码如下:

 此时编译会报错,认为这是一个protected类型的变量。

派生类可以调整基类protected类型变量的类型,但是不可以调整基类private类型变量的类型,因为基类protected类型的变量对派生类来说是可见的,但是private类型的变量则不可见。

报错,因为不能调整基类private类型变量的类型。

总结一下,就是派生类可以使用using调整它自己可见的基类成员的类型,但是对于它不可见的基类成员,是不能修改的

继承的控制关系

接下来看一下,派生类继承的控制关系。

基类成员在派生类中的访问控制属性如下:

基类成员在派生类中的访问控制属性
继承类型\基类访问属性publicprotectedprivate
publicpublicprotected隔离
protectedprotectedprotected隔离
privateprivateprivate隔离

表格中,第一列是继承的类型,第一行是基类的访问属性。

可以看到,public方式继承的话,是不会改变基类成员的控制属性的。而无论使用哪种继承方式,private类型的基类成员,都是处于隔离状态的。

但是,无论使用哪种继承方式,在派生类内部使用父类时无差别;不同的继承方式,会影响的是这两个方面:

  1. 外部代码对派生类的使用;
  2. 派生类的子类;

修改代码,分别使用三种类型继承。

他们继承的基类Father类定义如下。

测试

修改代码,在main函数中创建三个对象,然后分别调用基类Father的public类型成员——it_skill函数。

根据表格的描述,只有public方式继承的Son_pub可以在类外调用,其余的都会报错。

编译结果如下,符合预期。

但是如果不在main函数中调用,只在派生类内部调用,那么就不会有问题。

修改代码,在main函数中调用Son的play_game函数,在play_game函数中,会调用基类的protected类型成员(派生类内部调用)。

编译测试,没有问题,结果也符合预期。

 也就是说,无论使用哪种继承方式,在派生类内部使用父类时无差别。

覆写

在基类中有一个it_skill函数,如果在派生类中也有一个同名函数,那么会发生什么事情呢?

在Son_pub中创建一个it_skill函数,然后在main函数调用it_skill函数。

 

编译测试,可以看到调用的是派生类的it_skill函数,而不是父类的it_skill函数,也就是发生了覆写。

如果派生类没有it_skill函数,才会调用基类的it_skill函数。

派生类对象的空间分布

假设有一个基类Person,一个派生类Student。

那么,他们的成员的空间分布如下:

基类Person类有三个成员,派生类Student除了有继承自基类的三个成员外,还有自己定义的grade。

同时,在派生类中,还有一个和基类同名的函数printfInfo。

基类的printfIndo函数如下:

再写一个测试函数test_func,传入一个Person类的引用,函数中只是调用了一下对应的printfInfo函数。

main函数如下。

由于发生了覆写,所以s.printfInfo其实调用的是派生类自己的printfInfo函数而不是父类的。

需要注意的是,test_func函数传入的是Person类的引用,如果传入的是Person类的派生类Student,那么是否可以正常运行?

答:是可以的。根据前面的空间分布分析,Student类的分布,一部分是继承自基类,一部分则是来源于自己。

当调用test_func(s)时,传入的部分是属于基类的那部分,所以在test_func函数中调用的printfInfo函数,是基类的printfInfo函数,而不是派生类覆写的printfInfo函数。

只有通过s.printfInfo调用的,才是覆写的派生类自己的printfInfo函数。

测试结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值