c++继承—知识点汇总

C++中的继承概念,这是面向对象编程的一个重要特性

面向对象编程基于 4 个重要方面:封装、抽象、继承和多态。继承是一种强大的属性重用方式, 是通向多态的跳板。

在本章中,您将学习: • 编程意义的继承; • C++继承语法; • 公有继承、私有继承和保护继承; • 多继承; • 隐藏基类方法和切除(slicing)导致的问题。

1. 继承基础

  • 继承:一种机制,允许新创建的类(派生类)继承现有类(基类)的属性和方法。

  • 举例:在 Tom Smith 从祖先那里继承的东西中,最重要的是姓,因此他姓 Smith。另外,他还从父母那里 继承了某些价值观以及木雕手艺,因为 Smith 家族数代人都从事木雕行业。这些属性一起标识了 Tom 作为 Smith 家族后代的身份。 在编程领域,您经常会遇到具有类似属性,但细节或行为存在细微差异的组件。在这些情形下, 一种解决之道是将每个组件声明为一个类,并在每个类中实现所有的属性,这将重复实现相同的属性。 另一种解决方案是使用继承,从一个包含通用属性且实现了通用功能的基类派生出类似的类,并在派 生类中覆盖基本功能,以实现让每个类都独一无二的行为。第二种方法通常更佳。面向对象编程支持 继承,如图 10.1 所示。在这里插入图片描述

  • 派生类和基类之间的这种 is-a 关系仅适用于公有继承。本章首先介绍公有继承,让您明 白继承的概念以及最常见的继承方式,然后再介绍私有和保护继承

2. 继承和派生

  • 公有继承:使用public关键字,派生类继承基类的所有公有和保护成员,并且这些成员在派生类中保持原有的访问级别。
  • 私有继承:使用private关键字,基类的公有和保护成员在派生类中变为私有,不能被派生类的外部访问。
  • 保护继承:使用protected关键字,基类的公有和保护成员在派生类中变为保护,可以被派生类及其子类访问,但不能被派生类的外部访问。
  • 阅读介绍继承的文献时,您将遇到“从…继承而来”和“从…派生而来”等术语,它们的含义相同。 同样,基类也被称为超类;从基类派生而来的类称为派生类,也叫子类

3. C++派生语法

  • 派生类定义格式:

  • class Base 
    { 
     // ... base class members 
    }; 
    class Derived: access-specifier Base 
    { 
     // ... derived class members 
    }; 
    
  • 其中 access-specifier 可以是 public(这是最常见的,表示派生类是一个基类)、private 或 protected (表示派生类有一个基类)。

  • 下面的继承层次结构表明,Carp 类是从 Fish 类派生而来的:

    class Fish // base class 
    { 
     // ... Fish's members 
    }; 
    class Carp:public Fish // derived class 
    { 
     // ... Carp's members 
    }; 
    

4. 访问限定符

  • public:成员可以被任何访问。
  • private:成员只能被类本身访问。
  • protected:成员可以被类本身和其派生类访问。
  • **注意:**这个小程序表明,通过使用关 键字 protected,可对需要继承的基类属性进行保护,禁止在继承层次结构外部访问它。 这是面向对象编程的一个非常重要的方面,它与数据抽象和继承一起确保派生类可安全地继承基 类的属性,同时禁止在继承层次结构外部对其进行修改。

5. 构造顺序和析构顺序

  • 构造顺序:基类构造函数先于派生类构造函数执行。
  • 析构顺序:与构造顺序相反,先析构派生类,再析构基类。

6. 基类初始化

  • 如果基类包含重载的构造函数,需要在实例化时给它提供实参,该如何办呢?创建派生对象时将 如何实例化这样的基类?方法是使用初始化列表,并通过派生类的构造函数调用合适的基类构造函数, 如下面的代码所示:

  • class Base 
    { 
    public: 
         Base(int someNumber) // overloaded constructor 
         { 
         // Use someNumber 
         } 
    }; 
    Class Derived: public Base 
    { 
    public: 
         Derived(): Base(25) // instantiate Base with argument 25 
         { 
         // derived class constructor code 
         } 
    }; 
    
    

7. 在派生类中覆盖基类的方法

  • 如果派生类实现了从基类继承的函数,且返回值和特征标相同,就相当于覆盖了基类的这个方法, 如下面的代码所示:

    class Base 
    { 
    public: 
     void DoSomething() 
     { 
     // implementation code… Does something 
     } 
    }; 
    class Derived:public Base 
    { 
    public: 
     void DoSomething() 
     { 
     // implementation code… Does something else 
     } 
    };
    

8. 调用基类中被覆盖的方法

  • 使用作用域解析运算符::来调用基类中被覆盖的方法。

9. 隐藏基类的方法

  • 派生类中的方法如果与基类中的方法签名相同,会隐藏基类的对应方法。
  • 在派生类中隐藏基类的方法 覆盖的一种极端情形是,Tuna::Swim( )可能隐藏 Fish::Swim( )的所有重载版本,使得调用这些重载 版本会导致编译错误(因此称为被隐藏)

私有继承

  • 前面介绍的都是公有继承,私有继承的不同之处在于,指定派生类的基类时使用关键字 private:

    class Base  {  // ... base class members and methods  };  
    class Derived: private Base // private inheritance  {  // ... derived class members and methods  };
    

    私有继承意味着在派生类的实例中,基类的所有公有成员和方法都是私有的—不能从外部访问。 换句话说,即便是 Base 类的公有成员和方法,也只能被 Derived 类使用,而无法通过 Derived 实例来 使用它们。

保护继承

  • 保护继承不同于公有继承之处在于,声明派生类继承基类时使用关键字 protected:

    class Base  {  // ... base class members and methods  };  
    class Derived: protected Base // protected inheritance
    {  // ... derived class members and methods  
    }; 
    

    保护继承与私有继承的类似之处如下: • 它也表示 has-a 关系; • 它也让派生类能够访问基类的所有公有和保护成员; • 在继承层次结构外面,也不能通过派生类实例访问基类的公有成员。

切除问题

  • 如果程序员像下面这样做,结果将如何呢?

  • Derived objDerived;  
    
    Base objectBase = objDerived; 
    
  • 如果程序员像下面这样做,结果又将如何呢?

  • void UseBase(Base input);  ...  
    
    Derived objDerived;  
    
    UseBase(objDerived); // copy of objDerived will be sliced and sent  
    
  • 它们都将 Derived 对象复制给 Base 对象,一个是通过显式地复制,另一个是通过传递参数。在这 些情形下,编译器将只复制 objDerived 的 Base 部分,即不是整个对象。换句话说,Derived 的数据成 员包含的信息将丢失。这种无意间裁减数据,导致 Derived 变成 Base 的行为称为切除(slicing)。 要避免切除问题,不要按值传递参数,而应以指向基类的指针或 const 引用的方式传递。

10. 多继承问题

  • C++支持多继承,但可能导致菱形继承问题,需要使用虚继承来解决。在这里插入图片描述

11. 使用 final 关键字禁止继承

  • 使用final关键字可以阻止类被进一步继承。

  • class Platypus final: public Mammal, public Bird, public Reptile 
    { 
    public: 
     void Swim() 
     { 
     cout << "Platypus: Voila, I can swim!" << endl; 
     } 
    }; 
    
  • 除用于类外,还可将 final 用于成员函数来控制多态行为,这将在第 11 章讨论。

12. 构造函数和析构函数的其他用途

  • 例如,实现单例模式,禁止类对象被复制等。

13. 问与答

  • 问:我需要模拟哺乳动物(Mammal)以及一些具体的哺乳动物,如人(Human)、狮子(Lion)和鲸鱼(Whale)。我该使用继承层次结构吗?如果该使用,应使用哪种继承关系?
  • 答:鉴于人、狮子和鲸鱼都是哺乳动物,这是 is-a 关系,因此应使用公有继承,并将 Mammal 作为基类,而 Human、Lion 和 Whale 类从它派生而来。
  • 问:术语派生类和子类有何不同?
  • 答:是一回事,它们都表示从基类派生而来的类。
  • 问:以公有方式继承基类的派生类能访问基类的私有成员吗?
  • 答:不能。编译器总是执行最严格的访问限定符。无论继承关系如何,类的私有成员都不能在类外访问,一个例外是类的友元函数和友元类。

14. 应该与不应该

  • 要建立 is-a 关系,务必创建公有继承层次结构。 要建立 has-a 关系,务必创建私有或保护继承层 次结构。

  • 务必牢记,公有继承意味着继承派生类的类能够访问基类的公有和保护成员。可通过派生类的对象来访问基类的公有成员。

  • 务必牢记,私有继承意味着继承派生类的类也不能访问基类的成员。

  • 务必牢记,保护继承意味着继承派生类的类能够访问基类的公有和保护方法,但不能通过派生类的对象来访问基类的公有成员。

  • 务必牢记,无论继承关系是什么,派生类都不能访问基类的私有成员。

  • 不要仅为重用微不足道的方法而创建继承层次 结构。

  • 不要不分青红皂白地使用私有或公有继承,因为这可能给应用程序的可扩展性带来架构瓶颈。

  • 务必牢记,私有继承意味着继承派生类的类也不能访问基类的成员。

  • 务必牢记,保护继承意味着继承派生类的类能够访问基类的公有和保护方法,但不能通过派生类的对象来访问基类的公有成员。

  • 务必牢记,无论继承关系是什么,派生类都不能访问基类的私有成员。

  • 不要仅为重用微不足道的方法而创建继承层次 结构。

  • 不要不分青红皂白地使用私有或公有继承,因为这可能给应用程序的可扩展性带来架构瓶颈。

  • 在派生类中,不要编写与基类方法同名但参数 不同的方法,以免隐藏基类方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值