0
点赞
收藏
分享

微信扫一扫

【C++】面向对象之继承篇

王传学 2022-02-03 阅读 113

学习总结

(1)实例化Worker对象的时候,到底是先调用谁的构造函数
——先基类的构造函数,即要想实例化一个派生类,必须先(隐性)实例化一个基类。

在销毁Worker这个对象的时候,又是先调用谁的析构函数——是先子类的析构函数(和构造函数的顺序相反)。

(2)子类继承了父类后,观察是否继承了父类的数据成员和成员函数。Worker对象,子类可以访问父类的成员函数eat(),也可访问自己的成员函数work()

文章目录

一、继承

工人类属于人类,在定义工人类时为了不重复写部分代码,即两个类具包含关系。

class Worker:public Person{
public:
	void work();
	int m_iSalary;
};

PS :继承

(1)内存中的对象

在定义工人类时,就不需要再定义人类共有的属性。
在这里插入图片描述

(2)继承代码实践

/* ************************************************************/
/* 继承
要求:
​ 1.定义Person类
​ 数据成员:姓名(m_strName)和年龄(m_iAge)
​ 成员函数:构造函数、析构函数、eat()函数

​ 2.定义Worker类
​ 公有继承Person类,特有数据成员(工资 m_iSalary)
​ 成员函数:构造函数、析构函数、work()函数

​ 目的:
​ 1.实例化Worker对象的时候,到底是先调用谁的构造函数,
​ 在销毁Worker这个对象的时候,又是先调用谁的析构函数

​ 2.子类继承了父类后,观察是否继承了父类的数据成员和成员函数

文件结构为:
在这里插入图片描述

头文件Person.h

#include<string>
using namespace std;
class Person{
public:
	Person();
	~Person();
	void eat();
	string m_strName;
	int m_iAge;
};

头文件Worker.h

#include"Person.h"

class Worker:public Person{//Worker共有继承Person类
public:
	Worker();
	~Worker();
	void work();
	int m_iSalary;
};

Person.cpp

#include"Person.h"
#include<iostream>
using namespace std;

Person::Person(){
	cout<<"Person()"<<endl;
}
Person::~Person(){
	cout<<"~Person()"<<endl;
}

void Person::eat(){
	cout<<"eat"<<endl;
}

Worker.cpp

构造函数、析构函数、work()。

#include"Worker.h"
#include<iostream>
using namespace std;

Worker::Worker(){
	cout<<"Worker()"<<endl;
}
Worker::~Worker(){
	cout<<"~Worker()"<<endl;
}
void Worker::work(){
	cout<<"work()"<<endl;
}

主函数demo.cpp

#include<iostream>
#include<stdlib.h>
#include"Worker.h"
using namespace std;

int main(){
	Worker *p=new Worker();//堆
	delete p;
	p=NULL;
	system("pause");
	return 0;
}

结果为:
在这里插入图片描述

目的1 :调用顺序

实例化Worker对象的时候,到底是先调用谁的构造函数
——先基类的构造函数,即要想实例化一个派生类,必须先(隐性)实例化一个基类。

在销毁Worker这个对象的时候,又是先调用谁的析构函数——是先子类的析构函数(和构造函数的顺序相反)。

目的2:是否继承

2.子类继承了父类后,观察是否继承了父类的数据成员和成员函数。

#include<iostream>
#include<stdlib.h>
#include"Worker.h"

using namespace std;

int main()
{
    Worker *p = new Worker();
    p->m_strName = "Keiven";
    p->m_iAge = 20;
    p->eat();
    p->m_iSalary = 5000;
    p->work();
    delete p;
    p = NULL;
    system("pause");
    return 0;
}

从如下结果,看出Worker对象,子类可以访问父类的成员函数eat(),也可访问自己的成员函数work()
在这里插入图片描述

二、继承方式、隐藏

(1)公有继承

公有继承的方式时,按下表(注意:Protected成员是按照protected继承方式)

基类成员访问属性继承方式派生类成员访问属性
private成员public无法访问
protected成员protected
public成员public

(2)保护继承

保护继承时,按下表(注意:虽然基类的private成员会被继承,但也会无法访问)

基类成员访问属性继承方式派生类成员访问属性
private成员protected无法访问
protected成员protected
public成员protected

(3)私有继承

私有继承时,按下表(注意:虽然基类的private成员会被继承,但也会无法访问)

基类成员访问属性继承方式派生类成员访问属性
private成员private无法访问
protected成员private
public成员private
class Line{
public:
	Line(int x1,int y1,int x2,int y2);
private:
	Coordinate m_coorA;
	Coordinate m_coorB;
};

线段类Line只能访问到A点和B点这两个对象的共有数据成员和共有成员函数——Has a关系,即在线段中有一个坐标点的这种包含关系。

私有继承即子类的对象只能访问父类的共有数据和共有的成员函数——私有继承也是一种包含关系(Has a关系)。

(4)继承中的特殊关系

1)隐藏

如果父类A和子类B都定义了一个成员函数ABC(),由于子类B类继承A后即B中也拥有A中的成员函数ABC()
——此时子类B类中的ABC()函数会隐藏掉父类A中的ABC()函数。

隐藏的特性:父子关系、成员同名、隐藏。
(1)在实例化B的对象时,使用该对象只能够直接访问子类B中的ABC()成员函数,而无法访问A类的ABC()。
但是我们能够通过特殊手段访问。

(2)同名的隐藏不仅限于成员函数,还有同名的数据成员——不过父子类的数据成员同名没啥意义,所以少见。

class Person{
public:
	void play();
protected:
	string m_strName;
};
class Soldier:public Person{
public:
	void play();
	void work();
protected:
	int m_iCode;
};

上面例子就是父类和子类都有work成员函数,而真正访问的时候:

int main(){
	Soldier soldier;
	soldier.play();
	soldier.Person::play();//注意
	return 0;
}
不好的习惯:

让父类的数据成员和子类的数据成员同名,如Person类定义string code,Soldier类定义int code。
——因为2个数据成员都定义在protected下,即实例化的对象无法访问到子类和父类(protected成员是只有子类能访问)——而子类soldier的成员函数去使用code,访问的就是soldier类的code,如code="1234"

如果子类soldier的成员函数要访问父类Person继承下来的code数据成员,就必须使用Person::code="5678"

2)隐藏代码实践

/*******************************/
/* 继承关系中的隐藏
要求:
​ 1. Person类,数据成员:m_strName,成员函数:构造函数、play()
​ 2. Soldier类,数据成员:无,成员函数:构造函数、play()、work()
/*******************************/

在这里插入图片描述

头文件
#include<string>
using namespace std;
class Person{
public:
	Person();
	void play();
protected:
	string m_strName;
};

#include"Person.h"

class Soldier:public Person{
public:
	Soldier();
	void play();
	void work();
protected:
};
源程序

关键在于main函数的访问父类和子类的play()的两种写法

#include"Person.h"
#include<iostream>
using namespace std;

Person::Person(){
	m_strName="Mery";
}
void Person::play(){
	cout<<"Person---play()"<<endl;
	cout<<m_strName<<endl;
}
#include"Soldier.h"
#include<iostream>
using namespace std;

Soldier::Soldier(){
}
void Soldier::play(){
	cout<<"Soldier---play()"<<endl;
}
void Soldier::work(){
	cout<<"work()"<<endl;
}
#include<iostream>
#include<stdlib.h>
#include"Soldier.h"

using namespace std;
int main(){
	Soldier Soldier;
	Soldier.play();//用子类的play
	Soldier.work();
	Soldier.Person::play();//用父类的play
	system("pause");
}
运行结果&分析

在这里插入图片描述
关键在于main函数的访问父类和子类的play()的两种写法

探究是否参数问题

上面是父类和子类的同名函数play()的参数一样(都是无参),这次我们试试参数不同,看是否能有【隐藏】效果。
现对子类Soldier的play函数改为传入一个参数的:

void Soldier::play(int x){
	cout<<"Soldier---play()"<<endl;
}

而在主函数这次我们为了看能否用Soldier.play()的无参形式访问到父类的play(),用下面代码发现报错。

#include<iostream>
#include<stdlib.h>
#include"Soldier.h"

using namespace std;
int main(){
	Soldier Soldier;
	Soldier.play();//用子类的play
	Soldier.work();
	Soldier.Person::play();//用父类的play
	system("pause");
}
1>------ 已启动生成: 项目: person_soldier, 配置: Debug Win32 ------
1>  demo.cpp
1>c:\users\86493\desktop\王道ds\20210420\person_soldier\demo.cpp(8): error C2660: “Soldier::play”: 函数不接受 0 个参数
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0==========

由上面报错即知不用Soldier.play()的无参形式访问到父类的play(),要访问父类的play()只能用Soldier.Person::play()

数据成员同名

(5)is-a

1)什么是is-a

2)存储结构

3)is-a代码实践

三、多继承和多重继承

(1)多重继承

(2)多继承

(3)多重继承代码实践

(4)多继承代码实践

四、虚继承

(1)多继承+多重继承的烦扰

(2)虚继承

(3)虚继承编码实践

举报

相关推荐

0 条评论