7.1 基类和派生类

第7章 继承性和派生类

面向对象程序设计有两个重要机制:数据封装和继承。前几章主要介绍了数据封装的概念和实现方法,本章将深入探讨继承这一机制。

7.1 基类和派生类

本节讨论基类和派生类的基本概念及其定义格式。

7.1.1 派生类的定义

基本概念

在面向对象编程中,通过继承可以在一个已有类的基础上创建一个新的类。这个新类不仅继承了已有类的成员,还可以定义属于自己的新成员。已有的类称为基类,而新定义的类称为派生类子类

单继承

在C++中,一个派生类可以从一个基类派生,这种继承称为单继承。单继承的定义格式如下:

class 派生类名 : 继承方式 基类名 {
    // 派生类新定义成员
};
  • 派生类名:新定义类的名称。
  • 继承方式:指定继承类型的关键字,有以下三种:
    • public:公有继承,基类的公有和保护成员在派生类中保持其访问权限。
    • private:私有继承,基类的所有成员在派生类中都变为私有成员。
    • protected:保护继承,基类的公有和保护成员在派生类中变为保护成员。

例如:

class Base {
public:
    int a;
protected:
    int b;
private:
    int c;
};

class Derived : public Base {
    // 派生类的新成员和方法
};

在上述例子中,Derived类继承自Base类,并且使用公有继承方式。因此,Derived类可以直接访问Base类的公有成员a和保护成员b,但不能直接访问私有成员c

多继承

一个派生类也可以从多个基类派生,这种继承称为多继承。多继承的定义格式如下:

class 派生类名 : 继承方式1 基类名1, 继承方式2 基类名2, ... {
    // 派生类新定义成员
};

例如:

class Base1 {
public:
    int a;
};

class Base2 {
public:
    int b;
};

class Derived : public Base1, public Base2 {
    // 派生类的新成员和方法
};

在这个例子中,Derived类继承自Base1Base2类,并且使用公有继承方式。因此,Derived类可以直接访问Base1类的公有成员aBase2类的公有成员b

总结

派生类通过继承基类的成员和方法,实现了代码的重用性和扩展性。通过单继承和多继承,程序设计者可以灵活地构建复杂的类层次结构,满足不同的编程需求。接下来,我们将详细讨论不同的继承方式及其对访问权限的影响。

7.1.2 派生类的三种继承方式

在C++中,派生类可以通过三种继承方式从基类继承:公有继承(public)、私有继承(private)和保护继承(protected)。每种继承方式对基类成员在派生类中的访问权限有不同的影响。

1. 公有继承

公有继承的特点是:基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的访问权限,而基类的私有成员在派生类中是不可见的。

示例代码:

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};

class Derived : public Base {
    // publicMember 是公有成员
    // protectedMember 是保护成员
    // privateMember 在派生类中不可见
};

在以上示例中,Derived类可以直接访问Base类的公有成员publicMember和保护成员protectedMember,但不能访问私有成员privateMember

2. 私有继承

私有继承的特点是:基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。

示例代码:

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};

class Derived : private Base {
    // publicMember 是私有成员
    // protectedMember 是私有成员
    // privateMember 在派生类中不可见
};

在以上示例中,Derived类将Base类的公有成员publicMember和保护成员protectedMember都作为私有成员,不能被Derived的子类访问。

3. 保护继承

保护继承的特点是:基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,而不能为派生类的对象访问。

示例代码:

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};

class Derived : protected Base {
    // publicMember 是保护成员
    // protectedMember 是保护成员
    // privateMember 在派生类中不可见
};

在以上示例中,Derived类将Base类的公有成员publicMember和保护成员protectedMember都作为保护成员,只能被Derived的成员函数或友元函数访问。

小结

C++提供了三种继承方式:公有继承、私有继承和保护继承,每种继承方式对基类成员在派生类中的访问权限有不同的影响。选择合适的继承方式有助于合理设计类的层次结构,确保数据的封装性和继承关系的合理性。

 

7.1.3 基类成员在派生类中的访问权限

基类成员在派生类中的访问权限与继承方式有关。具体来说,不同的继承方式对基类成员的访问权限有不同的影响,如下所述:

公有继承
  • 基类的公有成员在派生类中仍然是公有成员。
  • 基类的保护成员在派生类中仍然是保护成员。
  • 基类的私有成员在派生类中不可访问。
私有继承
  • 基类的公有成员在派生类中成为私有成员。
  • 基类的保护成员在派生类中也成为私有成员。
  • 基类的私有成员在派生类中不可访问。
保护继承
  • 基类的公有成员在派生类中成为保护成员。
  • 基类的保护成员在派生类中仍然是保护成员。
  • 基类的私有成员在派生类中不可访问。

表7-1 不同继承方式的基类成员在派生类中的访问权限

继承方式基类特性派生类特性
公有继承 (public)publicpublic
公有继承 (public)protectedprotected
公有继承 (public)private不可访问
私有继承 (private)publicprivate
私有继承 (private)protectedprivate
私有继承 (private)private不可访问
保护继承 (protected)publicprotected
保护继承 (protected)protectedprotected
保护继承 (protected)private不可访问

记忆技巧

为了方便记忆,可以简单地记作:

  • 基类中私有成员不可访问。
  • 公有继承保持原有的访问权限(公有继承不变)。
  • 私有继承将所有成员变为私有(私有继承私有)。
  • 保护继承将所有公有和保护成员变为保护(保护继承保护)。

访问权限总结

  • 派生类只能访问公有继承方式下基类中的公有成员。
  • 派生类的派生类可以访问公有继承和保护继承方式下基类中的公有成员和保护成员。

7.1.4 成员访问权限的控制

前面讲述了在派生类中访问基类成员的权限规定。这里通过几个例子进一步讨论访问权限的具体控制。

[例7.1] 分析下列程序中的访问权限,并回答所提的问题
#include <iostream>
using namespace std;

class A {
public:
    void f1();
protected:
    int j1;
private:
    int i1;
};

class B : public A {
public:
    void f2();
protected:
    int j2;
private:
    int i2;
};

class C : public B {
public:
    void f3();
};

请回答下列问题:

  1. 派生类B中的成员f2()能否访问基类A中的成员:f1(), j1 和 i1?
  2. 派生类B的对象b能否访问基类A中的成员:f1(), j1 和 i1?
  3. 派生类C中成员函数f3()能否访问直接基类B中的成员:f2(), j2?能否访问间接基类A中的成员f1(), j1 和 i1?
  4. 派生类C的对象c能否访问直接基类B中的成员:f2(), j2?能否访问间接基类A中的成员:f1(), j1 和 i1?
  5. 从对问题(1)~(4)的回答可得出对公有继承的什么结论?

解答

  1. 可以访问f1()和j1,但不可以访问i1。
  2. 可以访问f1(),但不可以访问j1和i1。
  3. 可以访问直接基类中的f2()和j2,以及间接基类中的f1()和j1,但不可以访问i2和i1。
  4. 可以访问直接基类中的f2()和间接基类中的f1(),对其他成员都不可访问。
  5. 在公有继承时,派生类的成员函数可访问基类中的公有成员和保护成员;派生类的对象仅可访问基类中的公有成员。

思考:将程序中的两处继承方式的public改为private,又将如何回答上述各个问题?

[例7.2] 分析下列程序,并回答所提的问题
#include <iostream>
using namespace std;

class A {
public:
    void f(int i) {
        cout << i << endl;
    }
    void g() {
        cout << "g()" << endl;
    }
};

class B : A { // 缺省继承方式为 private
public:
    void h() {
        cout << "h()" << endl;
        A::f(6);
    }
};

int main() {
    B d1;
    d1.f(6);  // 语句1
    d1.g();   // 语句2
    d1.h();   // 语句3
}

请回答下列问题:

  1. 执行该程序时,哪个语句会出现编译错误?为什么?
  2. 去掉出错语句后,执行该程序后的输出结果如何?
  3. 程序中派生类B是从基类A继承来的,这种默认的继承方式是哪种继承方式?
  4. 派生类B中,A::f的含义是什么?
  5. 将派生类B的继承改为公有继承方式,该程序将输出什么结果?

解答

  1. 程序中,d1.f(6);语句会出现编译错误,因为B是以私有继承方式继承类A的,所以B类的对象不可访问A类的成员函数。
  2. 将程序中,d1.g();语句注释后,执行该程序输出如下结果:6 h()
  3. 使用class关键字定义类时,默认的继承方式是private
  4. A::f将基类中的公有成员声明为派生类的公有成员。
  5. class B : A改为class B : public A后,执行该程序输出如下结果:6 g() h()
[例7.3] 分析下列程序,并回答问题
#include <iostream>
#include <cstring>
using namespace std;

class A {
public:
    A(const char *nm) {
        strcpy(name, nm);
    }
private:
    char name[80];
};

class B : public A {
public:
    B(const char *nm) : A(nm) {}
    void PrintName() const;
};

void B::PrintName() const {
    cout << "name: " << name << endl;
}

int main() {
    B b1("wang li");
    b1.PrintName();
}

请回答下列问题:

  1. 执行该程序将会出现什么编译错误?
  2. 对出现的编译错误如何在访问权限上进行修改?
  3. 修改后使该程序通过编译,执行该程序后输出结果是什么?

解答

  1. 编译时出错行如下:cout << "name: " << name << endl;,错误信息提示name是私有成员不能访问。
  2. 在类A中,将private改写为protected,这样就可以通过编译。
  3. 执行修改后的该程序输出如下结果:name: wang li

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夏驰和徐策

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

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

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

打赏作者

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

抵扣说明:

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

余额充值