目录
在面向对象编程的主要目的之一是提供可复用的代码,这种行为有助于减少节省时间,并且可以减少在程序中引入更多的错误,继承就是代码复用的一种重要方法。
1.继承的使用
1.1 继承的格式
class child : public (父类名) {...}
public表示派生类是一个公有派生,基类的公有内容将成为派生类的公有内容,私有内容也会成为派生类的一部分,但是只能通过基类公有或者保护方法进行访问。
另外,如果使用private表示派生类是一个私有继承,使用protected表示派生类是一个保护继承。
这篇文章我们主要介绍最常见的公有继承。
但是在基类中,数据和方法也是具有访问权限的。
1.2 访问权限
基类数据访问权限分为三种:
1.public :public表示公有,派生类可以直接进行访问。
2.private: private表示私有,派生类只能通过基类的保护或者公有方法进行访问。
3.protected:protected表示保护,这个权限在派生类中才能体现他的与众不同,对于派生类,protected跟public一样,是可以直接进行访问的,但是对于外部,protected跟private一样,是不能够直接访问的。
1.3派生类和基类的关系
1.构造函数
派生类是依托于基类存在的,所以,我们创建派生类对象是必须先进行基类对象的创建,也就是说,基类的构造函数必须在派生类的构造函数之前进行完成,其中有两种方法完成这个任务
1.我们自己手动在派生类初始化列表进行基类的构造(可手动传参)。
2.我们自己不进行基类构造,让编译器自动调用基类的默认构造函数。
2.析构函数
销毁基类和派生类的顺序与构造函数的顺序是相反的,在进行析构时,程序将首先调用派生类析构函数,再进行基类构造函数。
3.派生类和基类的相互转换
将派生类引用或者指针转换为基类引用或者指针被称为向上强制转换,这是编译器默认进行的一种转换, 因此在函数传参中,如果函数参数是基类引用或者指针,我们可以传入参数为派生类或者基类的引用或者指针
但是我们想要将基类引用或者指针转换为派生类引用或者指针,如果不是有专门的转换函数或者使用强制类型转换,是不能够直接进行转换的。
2.虚函数
虚函数是继承中实现多态的一个重要方法,在基类中存在的方法到了派生类中,可能存在不同的行为模式,比如在计算机中,root账号在访问数据这个行为上,能够访问的范围就大大超过了普通账号。
2.1 虚函数的使用方法
虚函数的使用方法十分简单,就是在基类中需要在派生类中重新实现的方法前面加上关键字virtual,就能够让此函数成为虚函数,同时,在派生类中的此函数将自动成为虚函数,可以在派生类此函数前加virtual,也可以不加。如下:
class person {
public:
virtual void fun{...}
}
class child : public person{
public:
virtual void fun{...}
}
这样,我们就可以在基类和派生类中定义同一函数的不同行为,fun方法会根据对象的类型调用不同的fun函数行为。
2.2虚函数的实现原理
说到虚函数的实现方法,我们就必须来讲一下动态联编和静态联编了
程序调用函数时,将源代码中的函数调用解释为特定的函数代码的过程称为函数名联编,
在C++中,编译器必须查看函数名和函数参数才能确定使用的函数,这种在编译过程中完成函数名联编称为静态联编,也被称为早期联编。
但是,虚函数的存在使得这个工作更加困难,因为编译器在编译阶段不知道用户使用的是什么对象,因此需要在程序运行时才能够选出正确的虚函数代码,这就被称为动态联编,也被称为早期联编。
那么,实际上我们是如何寻找正确的虚函数实现方法的呢?
编译器处理虚函数的方法是给每一个对象添加一个隐藏成员,这个成员中保了一个指向函数指针数组的指针,这个数组我们称之为 虚函数表
下面引用c++ primer plus 的一段话:
基类对象包含一个指针,该指针指向基类中所有虚函数的地址表。派生类对象包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,派生类没有重新定义虚函数,该 虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则虚函数的地址也将被添加到 虚函数表中。注意,无论类中包含的虚函数是1个还是10个都只要在对象中添加1个地址成员,只是表的大小不同而已
2.3设置虚函数的原则
只有在派生类中我们需要重新定义的函数,我们才在基类中声明为虚函数,因为,虚函数对于时间开销更大,无缘无故设置虚函数会使程序的时间效率大大降低。
2.4 类中常见的虚函数
在类中,我们通常将析构函数设置为虚函数,因为这样能够保证我们将派生类以及基类的数据都进行释放,如下代码:
person* pp = new child; //child是person的派生类
delete pp;
如果在person 中的析构函数不是虚函数,那么这个delete只会调用person的析构函数,但是我们new的却是一个child的对象,这就会造成内存泄漏。
但是person中的析构函数是一个虚函数,我们根据对象,调用的是child的析构函数,在child的析构函数中会自动调用person的析构函数,两个析构函数都会进行,这就让数据完全被清理。
因此,我们在类中,即使不需要自定义析构函数,也应该将析构函数设置为虚函数。
3.抽象类
类可以分为两大类,抽象类和具体类,抽象类不可以实例化为对象,而具体类可以实例化为对象。
1.抽象类至少存在一个虚函数为纯虚函数
纯虚函数的声明只需要在虚函数声明处加上一个 =0 就可以实现,如下:
virtual void ViewAcct () =0;
纯虚函数可以实现,也可以不实现,其实实现纯虚函数是没有意义的,因为抽象类是不能实例化为对象的,纯虚函数是用来定义派生类的通用接口。
2.抽象类只可以做基类
抽象类存在纯虚函数,只可以作为基类