[黑马程序员课程记录]C++核心部分4

4.6 继承

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

有些类与类之间存在特殊的关系,例如下图中:

在这里插入图片描述

我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。

这个时候我们就可以考虑利用继承的技术,减少重复代码

4.6.1 继承的基本语法

例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同

接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处

普通实现:

#include<iostream>
using namespace std;
#include<string>

//普通实现页面

//java页面
class Java 
{
public:
	void header()
	{
		cout << "首页,公开课,登入,注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心,交流合作,站内地图...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "Java学科视频" << endl;
	}
};

class Python
{
public:
	void header()
	{
		cout << "首页,公开课,登入,注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心,交流合作,站内地图...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "Python学科视频" << endl;
	}
};

class Cpp
{
public:
	void header()
	{
		cout << "首页,公开课,登入,注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心,交流合作,站内地图...(公共底部)" << endl;
	}
	void left()
	{
		cout << "Java,Python,C++...(公共分类列表)" << endl;
	}
	void content()
	{
		cout << "C++学科视频" << endl;
	}
};
void test1()
{
	cout << "Java下载视频页面如下:" << endl;
	Java java;
	java.header();
	java.footer();
	java.left();
	java.content();

	cout << "-----------" << endl;
	Python python;
	python.header();
	python.footer();
	python.left();
	python.content();

	cout << "-----------" << endl;
	Cpp cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();
}



int main()
{
	test1();
	

	system("pause");
	return 0;
}

继承实现:

#include<iostream>
using namespace std;


//继承实现页面   
//公共部分
class basepage
{
	public:
	void header()
	{
		cout << "首页,公开课,登入,注册...(公共头部)" << endl;
	}
	void footer()
	{
		cout << "帮助中心,交流合作,站内地图...(公共底部)" << endl;
	}
	void left()
	{
		cout << "java,python,c++...(公共分类列表)" << endl;
	}
};
//继承好处 : 减少重复代码
// 语法: class 子类 :继承方式  父类
//子类  也称为  派生类
//父类  也称为  基类

//Java页面
class Java :public basepage
{
public:
		void content()
	{
		cout << "java学科视频" << endl;
	}
};
//Python页面
class Python :public basepage
{
public:
	void content()
	{
		cout << "Python学科视频" << endl;
	}
};
//C++页面
class Cpp :public basepage
{
public:
	void content()
	{
		cout << "CPP学科视频" << endl;
	}
};

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

	cout << "-----------" << endl;
	Python python;
	python.header();
	python.footer();
	python.left();
	python.content();

	cout << "-----------" << endl;
	Cpp cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();
}

int main()
{
	test1();
	

	system("pause");
	return 0;
}

总结:

继承的好处:可以减少重复的代码

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

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

一类是从基类继承过来的,一类是自己增加的成员。

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

4.6.2 继承方式

继承的语法:class 子类 : 继承方式 父类

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

在这里插入图片描述

示例:

#include<iostream>
using namespace std;


//继承方式

//公共继承
class base1
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class son1:public base1
{
public:
	void func()
	{
		m_a = 10;//父类中的公共权限成员  到子类依然是公共权限
		m_b = 10;//父类中的保护权限成员  到子类依然是保护权限
		//m_c = 10;//父类中的私有权限成员  子类访问不到
	}
	
};

class base2
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class son2 :protected base2
{
public:
	void func()
	{
		m_a = 10;//父类中的公共权限成员  到子类变为保护权限
		m_b = 10;//父类中的保护权限成员  到子类依然是保护权限
		//m_c = 10;//父类中的私有权限成员  子类访问不到
	}

};

class base3
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class son3 :private base3
{
public:
	void func()
	{
		m_a = 10;//父类中的公共权限成员  到子类变为私有权限
		m_b = 10;//父类中的保护权限成员  到子类变为私有权限
		//m_c = 10;//父类中的私有权限成员  子类访问不到
	}

};

class grandson3 :public son3
{
public:
	void func()
	{
		//m_a = 10;//在son3中已经是私有成员了
		//m_b = 10;
		//m_c = 10;
	}
};
void test1()
{
	son1 s1;
	s1.m_a = 100;
	//s1.m_b = 100;//保护权限,类外访问不到
	//s1.m_c = 100;
}

void test2()
{
	son2 s1;
	//s1.m_a = 100;//在son2中变为保护权限 类外访问不到
	//s1.m_b = 100;//保护权限,类外访问不到
	//s1.m_c = 100;
}

void test3()
{
	son3 s1;
	//s1.m_a = 100;//在son3中变为私有权限 类外访问不到
	//s1.m_b = 100;//在son3中变为私有权限 类外访问不到
	//s1.m_c = 100;
}


int main()
{
	test1();
	test2();
	test3();

	system("pause");
	return 0;
}
4.6.3 继承中的对象模型

**问题:**从父类继承过来的成员,哪些属于子类对象中?

示例:

#include<iostream>
using namespace std;


//继承方式

//公共继承
class base
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class son:public base
{
public:
	int m_d;
};
//利用开发者命令提示工具查看对象模型
//跳转盘符 c:
//跳转文件路径: cd 具体路径
//cl /d1 reportSingleClassLayout类名 文件名(139然后按下tab键)

void test1()
{
	cout << "sizeof son =: " << sizeof(son) << endl;//16
	//在父类中所以非静态成员都会被子类继承下去
	//父类中私有成员属性 是被编译器隐藏了 因此访问不到,但是确实被继承下去了
}


int main()
{
	test1();
	

	system("pause");
	return 0;
}

利用工具查看:

在这里插入图片描述

打开工具窗口后,定位到当前CPP文件的盘符

然后输入: cl /d1 reportSingleClassLayout查看的类名 所属文件名

效果如下图:

在这里插入图片描述

结论: 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到

4.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;
	}
};


void test1()
{
	base b;

	son s;
	//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
}


int main()
{
	test1();
	

	system("pause");
	return 0;
}

总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

4.6.5 继承同名成员处理方式

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

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域

示例:

#include<iostream>
using namespace std;


//继承中的构造和析构函数
class base
{
public:
	base()
	{
		m_a = 100;
	}

	void func()
	{
		cout << "base函数" << endl;
	}

	void func(int a)
	{
		cout << "base函数int" << endl;
	}

	int m_a;
};

class son:public base
{
public:
	son()
	{
		m_a = 200;
	}

	void func()
	{
		cout << "son函数" << endl;
	}

	int m_a;
};

//同名属性
void test1()
{
	son s;
	cout << "son :  " << s.m_a << endl;
	cout << "base ; " << s.base::m_a << endl;//如果通过子类对象访问父类成员,需要加作用域
}

//同名函数
void test2()
{
	son s;
	s.func();

	s.base::func();//如果通过子类对象访问父类函数,需要加作用域


	//s.func(100);//当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数
	s.base::func(100);//加作用域可以访问到父类中同名函数
}

int main()
{
	test1();
	test2();

	system("pause");
	return 0;
}

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
4.6.6 继承同名静态成员处理方式

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

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域

示例:

#include<iostream>
using namespace std;


//继承中同名静态成员的处理方式

class base
{
public:
	static void func()
	{
		cout << "Base - static void func()" << endl;

	}

	static void func(int a)
	{
		cout << "base - static void func(int)" << endl;

	}

	static int m_a;
};
int base::m_a = 100;

class son:public base
{
public:
	static void func()
	{
		cout << "son - static void func()" << endl;

	}

	static int m_a;
};
int son::m_a = 200;

void test1()
{
	son s;

	//1.通过对象访问
	cout << "通过对象" << endl;
	cout << "son  m_a= " << s.m_a << endl;
	cout << "base m_a= " << s.base::m_a << endl;

	//2.通过类名访问
	cout << "通过类名" << endl;
	cout << "son  m_a= " << son::m_a << endl;
	cout << "base m_a= " << son::base::m_a << endl;//第一个::表示类名方式访问  第二个::代表访问父类作用域下

}


void test2()
{
	son s;

	//通过对象
	cout << "通过对象" << endl;
	s.func();
	s.base::func();

	//通过类名
	cout << "通过类名" << endl;
	son::func();
	son::base::func();//第一个::表示类名方式访问  第二个::代表访问父类作用域下

	son::base::func(100);//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
}

int main()
{
	test1();
	test2();

	system("pause");
	return 0;
}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)

4.6.7 多继承语法

C++允许一个类继承多个类

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

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

示例:

#include<iostream>
using namespace std;


//多继承语法
//语法: class 子类:继承方式 父类1,继承方式 父类2 ...
class base1
{
public:

	base1()
	{
		m_a = 100;
	}
	int m_a;
};
class base2
{
public:
	base2()
	{
		m_a = 200;
		m_b = 200;
	}


	int m_a;
	int m_b;
};
class son :public base1, public base2
{
public:
	son()
	{
		m_c = 300;
		m_d = 400;
	}

	int m_c;
	int m_d;
};

void test1()
{
	son s;

	cout << "sizeof son =  " << sizeof(son) << endl;

	//cout << "m_a=  " << s.m_a << endl;//如果没有同名成员就直接就可以

	cout << "base 1 m_a=  " << s.base1::m_a << endl;
	cout << "base 2 m_a=  " << s.base2::m_a << endl;
}

int main()
{
	test1();


	system("pause");
	return 0;
}

总结: 多继承中如果父类中出现了同名情况,子类使用时候要加作用域

4.6.8 菱形继承

菱形继承概念:

​ 两个派生类继承同一个基类

​ 又有某个类同时继承者两个派生类

​ 这种继承被称为菱形继承,或者钻石继承

典型的菱形继承案例:

在这里插入图片描述

菱形继承问题:

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
    
  2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
    

示例:

#include<iostream>
using namespace std;

//动物类
class animal
{
public:

	int m_age;
};

//利用虚继承 解决菱形继承的问题
//继承之前 加上关键词virtual 变为虚继承
// animal 类称为虚基类
//羊类
class sheep:virtual public animal{};
//驼类
class tuo:virtual public animal{};
//羊驼
class sheeptuo :public sheep, public tuo{};

void test1()
{
	sheeptuo s1;
	//s1.m_age = 18;//不明确
	s1.sheep::m_age = 19;
	s1.tuo::m_age = 28;
	//当菱形继承,两个父类拥有相同数据,需要加作用域区分
	cout << "st.Sheep::m_Age = " << s1.sheep::m_age << endl;
	cout << "st.Tuo::m_Age = " << s1.tuo::m_age << endl;//都只有一份数据了
	cout << "st.m_Age = " << s1.m_age << endl;

	//这份数据我们知道,只有一份就行了,菱形继承导致数据有两份,资源浪费
	//使用虚继承
}

int main()
{
	test1();


	system("pause");
	return 0;
}

在这里插入图片描述

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值