C++(面向对象之继承、多态)
一、继承
前言
在c++里面,继承是指2个类之间的关系
例如:有一个org类,功能很完善,体量很大,突然有一天,需求发生改变,org类不能满足新的需求,我们的第一想法是,复制 org 类代码,粘贴成 new 代码,然后在new代码里面写新的函数,以满足新需求,但是我们有时无法复制粘贴,有可能体量太大,复制不方便。有可能该类我们没有源代码,没有办法去复制,所以我们应该使用继承,让new类继承自 org类,一旦继承过后,new类即使什么都不写,就会天然的拥有org类里面所有的数据和函数
在继承里面:
org类: 我们称为父类 或者 基类
new类:我们称为子类 或者 派生类
1、继承的语法格式
class org{
}
class new:public/protected/private org{
}
2、三种继承方式
继承类时有 public / protected / private 三种继承方式
1)不同继承方式下基类的访问权限
原有权限 \ 继承方式 | public | protected | private |
public | public | protected | private |
protected | protected | protected | private |
private | private(被隐藏) | private(被隐藏) | private(被隐藏) |
在继承中 protected 和 private 的区别
在继承过后,派生类无法内部访问基类的私有成员,但是派生类可以内部访问基类的受保护成员
2)继承的本质
其实继承就相当于复制粘贴,只是复制粘贴这一步是由编译器在编译那一步完成的
3、继承到底继承哪些内容
1)可以继承的内容
运算符重载函数、静态成员属性、静态成员方法、构造函数和析构函数
构造函数和析构函数要特别注意,虽然派生类继承了基类的构造函数和析构函数,但是并不是说将基类的构造函数和析构函数继承给派生类作为构造函数和析构函数,只是继承在派生类中,在构造和析构基类的对象时可以调用。
2)不能继承的内容
友元函数:是关于类的一种关系,并不是类的成员,自然不能继承
4、初始化及访问基类私有成员
1)初始化基类的私有成员
私有成员不论是派生类还是基类,都只能类中访问,构造类中的成员也只能由自己的构造函数完成
因此可以在派生类的初始化列表中调用基类的构造函数来初始化。
注意:派生类只能调用直接基类的构造函数,不能夸辈调用构造函数。
例如:A继承B,B继承C,A可以调用B的构造函数,但是不能调用C的构造函数
2)访问基类私有成员
外部访问类的私有成员需要类中设有公开接口才能访问,派生类访问基类的私有成员也是一样的
5、基类、派生类、派生类中的对象的构造顺序
在构建派生类对象时,按照执行顺序,会先构造基类,再构建派生类中的对象,最后是派生类自己
构造的顺序是:
基类 -> 派生类中的对象 -> 派生类本身
析构的顺序是:(遵循先进后出原则,先构建的后析构)
派生类本身 -> 派生类中的对象 -> 基类
6、基类、派生类中的重名函数或变量
7、多重继承
8、棱形继承
1)棱形继承存在的问题
2)virtusl(虚继承)
二、多态
前言
多态是指,基类可以调用多个不同派生类对象的成员方法的能力
1、多态的实现
1)多态是通过virtual (虚函数) 来实现的(步骤)
- 声明虚函数:在基类中用关键字virtual 声明虚函数
- 重写虚函数:将派生类中要调用的函数名重写,与虚函数名同名同参
- 通过引用/指针调用:使用基类类型的指针或引用指向派生类的对象,并调用基类的虚函数
2)示例
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <vector>
#include <memory>
#include <unistd.h>using namespace std;class Core{private:public:virtual void func()//声明虚函数{cout << "基类函数" << endl;}
};class Core_pro:public Core{private:public:void func()//将想要调用的函数写为和虚函数同名同参的函数{cout << "派生类函数1" << endl;}
};class Core_pro_max:public Core{private:public:void func()//将想要调用的函数写为和虚函数同名同参的函数{cout << "派生类函数2" << endl;}
};class Person{private:public://以引用类型接收变量,调用不同派生类的成员方法void work1(Core& c){c.func();}//以指针类型接收变量,调用不同派生类的成员方法void work2(Core* c){c->func();}
}; int main(int argc,const char** argv)
{Core c;Core_pro cp;Core_pro_max cpm;Person zs;//传入变量zs.work1(c);zs.work1(cp);zs.work1(cpm);//传入地址zs.work2(&c);zs.work2(&cp);zs.work2(&cpm);return 0;
}
作业练习:
1:写一个Msg消息队列类,要求有以下功能Msg m() 构造函数,创建消息队列m[1] << "hello" 向消息队列的1频道写入数据"hello",当然可以是其他的字符串数据m[2] >> str :从2频道读取消息,并且将读到的消息存入str中析构函数:删除消息队列
2:写一个员工类 Employee,有一个多态函数叫做 getSalary有一个 Cleanner 保洁类,继承自员工类:每个月获得 5000 工资有一个 Coder 程序员类,继承自员工类,每个月获得 10000工资有一个 Manger 经理类,继承自员工类,每个月获得 15000工资写一个发工资的函数,要求,能够为所有员工发放工资,即使追加新的岗位,也不会改变这个函数的逻辑
3:写一个基类叫做颜色写3个派生类,红绿蓝以及需要存在的其他颜色,继承自颜色类要求实现以下功能:Red rGreen g;Color new_color = r + gnew_color.show() ;// 终端输出 "黄色"