初级C++3继承、多态、异常、命名空间


面向对象特点:抽象 封装 继承 多态

可重用型 software reusability

继承 inheritance

作用:在一个已存在的类的基础上建立一个新的类

已存在的类:父类 father class  基类 base class

           新类:子类 son class    派生类 derived class


 单继承 single inheritance:一个派生类只从一个基类派生  树结构

多重继承 multiple inheritance:一个派生类有两个或以上基类  图结构


派生类是基类的具体化

基类是派生类的抽象


派生类的声明方式:

class 派生类名:[继承方式] 基类名

{派生类新增加的成员};


继承方式:

    |--public  公用的

    |--private    私有的(默认)

    |--protected  受保护的


派生类的构成:

    |--从基类继承过来的成员  共性

    |--自己增加的成员     个性


构造派生类的3个步骤:

1、从基类接受成员(不包括构造函数和析构函数) 谨慎定义基类,因为会造成冗余

2、调整从基类接受的成员(公用变私有,重载)

3、增加派生类的个性成员,定义构造函数和析构函数


——————————————————————————————————


基类中的成员  公用派生类  私有派生类  保护派生类

    私有成员     不可访问      不可访问      不可访问

    公用成员     公用              私有             保护

    保护成员     保护              私有            保护



派生类中的成员有4种访问属性

派生类的成员   派生类中  派生类外部  下层派生类

    公用成员        1                 1                1

  受保护成员       1                0                1

    私有成员        1                 0                0

不可访问成员     0                 0                0


私有继承和保护继承不常用

实际中常用的是公用继承


———————————————————————————————————


派生类的构造函数

派生类不能继承基类的构造函数和析构函数

在执行派生类的构造函数时,调用基类的构造函数



简单派生类的构造函数

派生类构造函数名(总参数列表):基类构造函数名(参数列表)  //类似于函数初始化列表

{ 派生类中新增数据成员初始化语句 }

先调用基类构造函数,再执行派生类构造函数

先执行派生类析构函数,再执行基类析构函数


有子对象 subobject 的派生类的构造函数

派生类构造函数名(总参数列表):基类构造函数名(参数列表),子对象名(参数列表)

{ 派生类中新增数据成员初始化语句 }

顺序:

调用基类构造函数

调用子对象构造函数

执行派生类本身构造函数


对于多层派生的构造函数,只需写出其上一层派生类/直接基类的构造函数即可


析构函数

先执行派生类自己的析构函数

再执行子对象的析构函数

最后执行基类的析构函数


———————————————————————————————————


多重继承 multiple inheritance

允许一个派生类同时继承多个基类


多重继承派生类的构造函数

派生类构造函数名(总参数列表):基类1构造函数(参数列表),基类2构造函数(参数列表),基类3构造函数(参数列表)

{ 派生类中新增数据成员初始化语句 }


调用基类构造函数的顺序与声明顺序相同


多重继承引起的二义性 ambiguous

解决方法 在访问时加 基类名::变量/函数


———————————————————————————————————


虚基类 virtual base class

A派生B C

D多继承B C

虚基类让D只保存一份A的数据

在继承间接共同基类时,只保留一份成员


class A

{};

class B:virtual public A

{};

class C:virtual public A

{};

class D:public B, public A

{};


虚基类不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的

    class 派生类名:virtual 继承方式 基类名


为保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。


D的构造函数:D():A(),B(),C()

在最后的派生类中,负责对虚基类 直接基类的初始化

编译系统只执行最后的派生类对虚基类的构造函数的调用,保证了虚基类的数据成员不会被多次初始化


使用多重继承要十分小心二义性问题。

不提倡在程序中使用多重启程,能用单继承就不用多重继承。


———————————————————————————————————


基类与派生类的转换


只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。

不同类型数据之间的自动转换和赋值,称为赋值兼容

基类与派生类对象之间有赋值兼容关系

1、派生类对象可以向基类对象赋值;

2、派生类对象可以向基类对象的引用进行赋值或初始化;//此引用对应派生类中基类的部分

3、如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象;

4、指向基类对象的指针可以指向派生类对象。


——————————————————————————————————


组合 composition

在一个类中以另一个类的对象作为数据成员 has a   继承 is a


———————————————————————————————————


继承在软件开发中的重要意义

缩短软件开发过程的关键是  软件重用

典型例子:类库

对类库中类的声明一般放在头文件中

类的实现(函数的定义部分)单独编译,以代码形式存放在系统某一目录下


——————————————————————————————————


多态性与虚函数


多态性 polymorphism

一个事物有多种形态

向不同的对象发送同一个消息,不同的对象在接受时会产生不同的行为


从系统实现角度分类:

    |--静态多态性 编译时多态 函数重载

    |--动态多态性 运行时多态 虚函数 virtual function


虚函数的作用是允许在派生类中重新定义与基类同名的函数,

并且可以通过基类指针或引用来访问基类和派生类中的同名函数。


通过指向基类的指针,指向基类或不同的派生类对象,可以调用各自的虚函数


virtual关键字

1、在基类中用virtual声明成员函数为虚函数,在类外定义虚函数时,不必再加virtual;

2、当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动称为虚函数

   建议在派生类的虚函数前加virtual

3、虚函数与指向基类对象的指针变量配合使用


函数重载:横向  首部不同(参数不同)

    虚函数:纵向  首部相同


编译系统要根据已有的信息,对同名函数的调用做出判断。

确定调用的具体对象的过程称为关联(binding)

把一个函数名与一个类对象捆绑在一起,建立关联

把一个标识符和一个存储地址联系起来


静态关联 static binding  

在编译时即可确定其调用的虚函数属于哪一类

函数重载属于静态关联  studl.diaplay() gradl.display()均属于静态关联

运行前关联 早期关联 early binding


动态关联 dynamic binding

在运行阶段把虚函数和类对象binding在一起

这种多态性是动态的多态性,运行阶段的多态性

运行阶段关联 滞后关联 late binding


说明:

使用虚函数,系统要有一定的空间开销

当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table)

它是一个指针数组,存放每个虚函数的入口地址

系统在进行动态关联时的时间开销是很少的,因此多态是高效的。


———————————————————————————————————


如果析构函数未声明为虚函数

在撤销动态存储空间时,仅能调用基类的析构函数。


建议将基类的析构函数声明为虚函数

保证在撤销动态存储空间时能得到正确处理。


———————————————————————————————————


纯虚函数与抽象类


纯虚函数 pure virtual function  

在声明虚函数时被“初始化”为0的函数


一般形式:

virtual 函数类型 函数名(参数列表)=0;


注意:

1、纯虚函数没有函数体

2、最后面的“=0”仅起形式上的作用,告诉编译系统“这是纯虚函数”

3、声明语句加分号



抽象类 abstract class

不用来定义对象而只作为一种基本类型用作继承的类  

由于它用作基类,通常称为抽象基类 abstract base class


凡是包含纯虚函数的类都是抽象类

因为虚函数不能被调用,所以无法建立对象


抽象类的作用是作为一个类族的共同基类,为一个类族提供一个公共接口。


可以建立对象的类称为具体类 concrete class


———————————————————————————————————


异常处理

程序中两大类错误:

    |--编译错误/语法错误

    |--运行错误

        |--除0

        |--内存不足

        |--无法打开文件

        |--数据类型出错


在设计程序时,应当事先分析程序运行时可能出现的各种意外情况,并分别制定相应的处理方法,这就是程序的异常处理的任务


如果在执行一个函数过程中出现异常,可以不在本函数中立即处理,而是发出一个信息传给它的上一级(调用它的函数)

它的上级捕捉到这个信息后进行处理,如果处理不了,继续向上级抛,、

如果最高一级也无法处理,终止程序。


异常处理机制的3个部分:

try 检查

catch 捕捉

throw 抛出 


throw抛出什么样的数据由程序设计者自定,可以是任何类型的数据(包括自定义类型的数据,如类对象,结构体)


说明:

1、被检测的语句必须放在try块中,否则不起作用

2、try块和catch块作为一个整体出现,可以有try无catch

3、try catch 后必须跟{}

4、只能有一个try,可以有多个catch

5、catch后面的圆括号中,一般只写异常信息的类型名

        catch(double)

        catch(double d) //可以利用throw传递过来的具体值

6、catch(...) 捕捉任何类型的异常信息

7、throw;此语句代表:我不处理这个异常,请上级处理

9、如果throw抛出的异常信息找不到与之匹配的catch块,那么系统就会调用一个系统函数terminate,使程序终止运行


在函数声明中进行异常情况的指定

double triangle(double, double, doubl); //可抛任何类型的异常

double triangle(double, double, double) throw(double); //可抛double型异常

double triangle(double, double, double) throw(double, int, char, float); //可抛double int char float类型的异常

double triangle(double, double, double) throw(); //不能抛出异常


在异常处理中处理析构函数

C++的异常处理机制会在throw抛出异常信息被catch捕获时,对有关的局部对象进行析构

析构对象的顺序与构造的顺序相反

然后执行与异常信息匹配的catch块中的语句


——————————————————————————————————


命名空间

命名空间是可以由用户命名的作用域,用来处理程序中常见的同名冲突


所谓命名空间,实际上就是一个由程序设计者命名的内存区域。

程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。

namespace ns    //namespace 关键字

{

    int a;

    double b; //a b 称为命名空间成员 namespace member

}


ns::a

ns::b  //命名空间限定 qualified  被限定名 qualified name


命名空间的作用类似于目录与文件的关系


命名空间的作用是建立一些相互分隔的作用域,把一些全局实体分割开来,以免产生名字冲突。


全局变量在全局命名空间,独立于所有有名的命名空间之外,它不需要用namespace声明,由系统隐式声明,存在于每个程序之中。


命名空间可以包括:

    变量(可以带有初始化)

    常量

    函数(定义或者声明)

    结构体

    类

    模板

    命名空间(嵌套命名空间)


使用命名空间成员的方法

1、命名空间名::命名空间成员名

2、使用命名空间别名

    namespace 别名 = 命名空间名;

3、using 命名空间成员名

   using std::cout;

4、using namespace 命名空间名

   using namespace std;


无名的命名空间

file

namespace

{

    void fun()

    {...}

}


fun();

在无名的命名空间中的东西只在本文件的作用域中有效。C++

对全局变量用static声明使其只在本文件有效。C



标准命名空间 std

存放标准库的有关内容的命名空间

C++库的所有标识符都是在一个名为std的命名空间中定义的

或者说

标准头文件中函数、类、对象、类模板是在命名空间std中定义的


C传统方法                C++新方法

#include<stdio.h>        #include<cstdio>

#include<math.h>         #include<cmath>

#include<string.h>       #include<cstring>

                                         using namespace std;

无需命名空间             需要命名空间 



转载于:https://my.oschina.net/kuailechengxuyuan/blog/646010

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值