C++面向对象(类和对象)—— 对象特性(继承、封装、多态)

在这里插入图片描述
在这里插入图片描述

🎁个人主页:工藤新一¹

🔍系列专栏:C++面向对象(类和对象篇)

​ 🌟心中的天空之城,终会照亮我前方的路

🎉欢迎大家点赞👍评论📝收藏⭐文章

六、继承

  • 继承面向对象的三大特性之一

  • 优点:减少大量而重复的代码

  • 语法:class 子类(派生类) : 继承方式 父类(基类)

  • 派生类中的成员包含两大部分:

    1.一类是从基类继承过来的,另一类是自己增添的成员

    2.基类继承过来的特性表现其共性,而新增的成员体现了其个性

在这里插入图片描述


6.1继承的基本语法

  • 例如在我们看到的很多网站中,都有公共的头部,公共的底部,或者公共的左右侧列表,只有中间内容各显神态
  • 接下来我们分别要利用普通写法继承的方式来实现网页中的内容,来对比一下继承存在的意义,以及其所具有的便利性

在这里插入图片描述


  • 写一个普通实现的 Java教学页面
#include<iostream>
using namespace std;

class Java {
public:
	void header(){
		cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
	}

	void footer(){
		cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
	}

	void left(){
		cout << "Java、C++、C、Python...(公共分类列表)" << endl;
	}

	void content() {
		cout << "Java 学科视频(页面内容)" << endl;
	}
};

void test1()
{
	cout << "Java 下载视频页面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
}
int main(){test1();return 0;}

在这里插入图片描述


  • Python、C++ 教学页面
#include<iostream>
using namespace std;

class Java {
public:
	void header(){
		cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
	}

	void footer(){
		cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
	}

	void left(){
		cout << "Java、C++、C、Python...(公共分类列表)" << endl;
	}

	void content() {
		cout << "Java 学科视频(页面内容)" << endl;
	}
};

class Python {
public:
	void header() {
		cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
	}

	void footer() {
		cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
	}

	void left() {
		cout << "Java、C++、C、Python...(公共分类列表)" << endl;
	}

	void content() {
		cout << "Python 学科视频(页面内容)" << endl;
	}
};

class Cpp {
public:
	void header() {
		cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
	}

	void footer() {
		cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
	}

	void left() {
		cout << "Java、C++、C、Python...(公共分类列表)" << endl;
	}

	void content() {
		cout << "Python 学科视频(页面内容)" << endl;
	}
};

void test1()
{
	cout << "Java 下载视频页面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "-----------------------------------" << endl;
}
void test2() {
	cout << "Python 下载视频页面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "-----------------------------------" << endl;
}

void test3()
{
	cout << "Cpp 下载视频页面如下:" << endl;
	Cpp cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();
	cout << "-----------------------------------" << endl;
}
int main(){test1();test2();test3();return 0;}

在这里插入图片描述

  • 其实我们可以看到,通过笨拙的方法依然可以得到我们想要的内容,但这样的方法不管是在效率上、空间上…都不尽人意。

  • 继承的高效性 — 继承实现页面
#include<iostream>
using namespace std;

class BasePage//公共部分
{
public:
	void header(){
		cout << "首页、公开课、登录、注册...(公共头部信息)" << endl;
	}

	void footer(){
		cout << "帮助中心、交流合作、站内地图...(公共底部信息)" << endl;
	}

	void left(){
		cout << "Java、C++、C、Python...(公共分类列表)" << endl;
	}

};

//Java页面:Java 继承(:) BasePage的内容
class Java : public BasePage {
public:
	void content() {
		cout << "Java 学科视频" << endl;
	}
};

//Pthyon页面:Pthyon 继承(:) BasePage
class Python : public BasePage {
public:
	void content() {
		cout << "Pthyon 学科视频" << endl;
	}
};

//Cpp页面:Cpp 继承(:) BasePage
class Cpp : public BasePage {
public:
	void content() {
		cout << "C++ 学科视频" << endl;
	}
};

void test() {
	cout << "Java 下载视频页面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "-----------------------------------" << endl;

	cout << "Python 下载视频页面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "-----------------------------------" << endl;

	cout << "C++ 下载视频页面如下:" << endl;
	Cpp cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();
	cout << "-----------------------------------" << endl;
}
int main(){test();return 0;}

在这里插入图片描述


6.2继承方式

  • 继承语法:class 派生类 : 继承方式 基类

  • 继承方式一个有三种:

    • 公共继承
    • 保护继承
    • 私有继承
  • 继承方式图示:

在这里插入图片描述


  • 公共继承

在这里插入图片描述

在这里插入图片描述


  • 保护继承
    • 子类所继承的父类可访问的成员(或行为)变为Protected

在这里插入图片描述

在这里插入图片描述


  • 私有继承
    • 子类继承的可访问的父类成员变为Private

在这里插入图片描述

在这里插入图片描述


6.3继承中的对象模型

  • 问题:从父类继承而来的成员,哪些属于父类?
#include<iostream>
using namespace std;

class Base {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;//父类中的私有成员,在子类被隐藏了,但仍可被子类继承
};

//公共继承
class Son1 : public Base {
public:
	int m_D;
};

void test01() {
	cout << "sizeof Son1 == " << sizeof(Son1) << endl;
}
int main(){test01();return 0;}

在这里插入图片描述

  • 根据代码块的输出,我们可以验证:
    • 父类中私有成员,在子类被隐藏了,但仍可被子类继承
  • **结论:**父类中所有非静态成员属性都会被子类继承下去

  • 继承对象模型分布图: VS开发人员命令提示工具查看对象模型
  • **跳转盘符 F: **
  • 跳转文件路径:cd 具体路径
  • 查看命令行:cl / d1 reportSingleClassLayout 类名 “文件名”

在这里插入图片描述


  • 结论:父类中的私有成员,同样被子类所继承,只是编译器将其隐藏,因此我们访问不到父类中的私有成员

在这里插入图片描述


6.4继承中构造与析构顺序

  • 子类继承父类后,当创建子类对象时,也会调用父类中的构造函数

  • 问题:父、子类间的构造和析构顺序先后顺序?

  • 先构造父类,再构造子类,且在调用子类时,会优先调用子类所属的父类的构造函数,反之调用析构函数

#include<iostream>
using namespace std;

class Base {
public:
	Base() {
		cout << "Base 的构造函数调用!" << endl;
	}

	~Base() {
		cout << "Base 的析构函数调用!" << endl;
	}

};

class Son : public Base {
public:
	Son() {
		cout << "Son 的构造函数调用!" << endl;
	}

	~Son() {
		cout << "Son 的析构函数调用!" << endl;
	}
};

int main(){ Son s;return 0; }

在这里插入图片描述

  • 图示原理:

在这里插入图片描述


6.5同名继承的处理方式

  • 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的对象呢?

  • 访问子类同名成员,直接访问即可

  • 访问父类同名成员,需加入作用域

6.5.1同名成员

  • 同名成员变量(成员属性):
#include<iostream>
using namespace std;

class Base {
public:
	
	Base() {
		m_A = 100;
	}

	int m_A;
};

class Son : public Base {
public:

	Son(){
		m_A = 200;
	}

	int m_A;
};

void test() {
	Son s;
	cout << "Son  下的 s.m_A == " << s.m_A << endl;
	cout << "Base 下的 s.m_A == " << s.Base::m_A << endl;
}
int main(){test();return 0;}

在这里插入图片描述


  • 同名成员函数(成员行为):
class Base {
public:
	
	void func() {
		cout << "Base - func 的调用!" << endl;
	}

	int m_A;
};

class Son : public Base {
public:

	int m_A;
};

//当不存在 Son - func() 时,子类 Son 所实例化出 s,可以直接访问父类 Base 中的公共属性、行为 
void test() { Son s; s.func(); }

  • 当子类与父类具有同名的成员函数 - 通过对象直接调用,子类会优先调用自身的成员函数,因此需要以作用域,加以区分
#include<iostream>
using namespace std;

class Base {
public:
	
	void func() {
		cout << "Base - func 的调用!" << endl;
	}

//函数重载:同一作用域下,函数的名称相同,但ha
    void func(int) {
	cout << "Son - func(int) 的调用!" << endl;
}
	int m_A;
};

class Son : public Base {
public:

	void func() {
		cout << "Son - func 的调用!" << endl;
	}
	int m_A;
};

void test() { 
    Son s; 
    s.func(); 
	s.Base::func();
    s.Base::func(100);//函数重载
}

在这里插入图片描述


6.5.2继承同名静态成员处理方式

  • 问题:继承中同名的静态成员在子类对象上如何进行访问?

  • 静态成员与非静态成员同名,处理方式一致

    • 访问子类同名成员,直接访问即可

    • 访问父类同名成员,需加入作用域

  • 静态同名成员属性:

    • 通过对象访问
    • 通过类名访问
#include<iostream>
using namespace std;

class Base {
public:
	static int m_A;//类内声明,类外初始化
};
int Base::m_A = 100;

class Son : public Base {
public:
	static int m_A;
};
int Son::m_A = 200;

void test() {
	//1.通过对象访问数据
	cout << "通过对象访问:" << endl;
	Son s;
	cout << "Son  - m_A == " << s.m_A << endl;
	cout << "Base - m_A == " << Base::m_A << endl;

	//2.通过类名访问数据
	cout << "通过类名访问数据:" << endl;
	cout << "Son  - m_A == " << Son::m_A << endl;
	//Son::(通过Son类名的访问方式) - Base::m_A(访问父类Base作用域下的m_A)
	cout << "Base - m_A == " << Son::Base::m_A << endl;
}
int main(){test();return 0;}

  • 同名静态成员函数(行为):

    • 通过对象访问

    • 通过类名访问

#include<iostream>
using namespace std;

class Base {
public:

	static void func() {
		cout << "Base - static func()的调用!" << endl;
	}

	static void func(int) {
		cout << "Base - static func(int)的调用!" << endl;
	}
};

class Son : public Base {
public:
	static void func() {
		cout << "Son - static func()的调用!" << endl;
	}
};

void test() {
	//1.通过对象访问
	cout << "通过对象访问:" << endl;
	Son s;
	s.func();
	s.Base::func();

	//2.通过类名访问
	cout << "通过类名访问:" << endl;
	Son::func();
	Son::Base::func();
	Son::Base::func(100);
}
int main(){test();return 0;}

6.6多继承语法

  • C++允许一个类继承多个类(认多个爹)

  • 语法:class 子类 : 继承方式 父类1, 继承方式2 父类2

  • 多继承可能会引发父类中有同名成员出现,需要加作用域区分
    C++实际开发中不建议使用多继承

#include<iostream>
using namespace std;

class Base1 {
public:
	Base1() {
		m_A = 100;
	}

	int m_A;
};

class Base2 {
public:
	Base2() {
		m_B = 200;
	}

	int m_B;
};
//子类继承Base1 和 Base2
class Son : public Base1, public Base2{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}

	int m_C;
	int m_D;
};

void test(){
	Son s;
	cout << "sizeof(s) == " << sizeof(s) << endl;
}
int main(){test();return 0;}

在这里插入图片描述

  • 总结:多继承中如果父类中出现了同名情况,子类调用时要加入作用域做区分

6.7菱形继承

  • 菱形继承的概念:

    1.两个派生类继承他一个基类

    2.又有某个类同时继承两个派苏类

    3.这种继承就被称为菱形继承,或钻石继承

在这里插入图片描述


  • 菱形继承的典例:

在这里插入图片描述


  • 菱形继承问题:

    1.羊继承了动物数据,驼同样继承了动物数据,当草密码使用数据时,救就会产生二义性

    2.草泥马继承来自动物类的两份数据,但其实我们清楚,这份数据我们只需要一份就够了

#include<iostream>
using namespace std;

class Animal//动物类
{
public:

	int m_Age;
};

class Sheep : public Animal//羊类
{
public:

};

class Tuo : public Animal//驼类 
{
public:

};

class SheepTuo : public Sheep, public Tuo {
public:

};
void test() {
	SheepTuo st;
	st.m_Age = 100;
}
int main(){test();return 0;}
  • 二义性:出现菱形继承,两个父类拥有相同数据,需加以作用域区分

在这里插入图片描述

void test() {
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 200;
    cout << "st.Sheep::m_Age == " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age == " << st.Tuo::m_Age << endl;
}

在这里插入图片描述


  • 年龄这份数据,我们知道只要有一份就够了,但菱形继承却导致这份数据有两份,造成资源上的浪费

在这里插入图片描述


6.8虚继承 — 解决菱形继承问题

  • 继承前加入关键字:virtual ---> 变为虚继承,而其父类被称为虚基类
class Animal{
public:

	int m_Age;
};

//class Sheep : virtual public Animal就是虚继承
class Sheep : virtual public Animal//此时Animal就被称为虚基类
{
public:

};

class Tuo : virtual public Animal{
public:

};

class SheepTuo : public Sheep, public Tuo {
public:

};
void test() {
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 200;
	cout << "st.m_Age == " << st.m_Age << endl;
	cout << "st.Sheep::m_Age == " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age == " << st.Tuo::m_Age << endl;
}
int main(){test();return 0;}

在这里插入图片描述

  • m_Age被归为同一份数据,且st.m_Age也不再产生二义性(因为只有一份m_Age数据)。

在这里插入图片描述

在这里插入图片描述


总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义的代码块
  • 利用虚继承可以解决菱形继承的问题

🌟 各位看官好我是工藤新一¹呀~

🌈 愿各位心中所想,终有所致!
在这里插入图片描述

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值