前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

继承

作者头像
DeROy
修改2020-08-19 11:02:51
6940
修改2020-08-19 11:02:51
举报
文章被收录于专栏:编程学习基地

继承

继承语法

代码语言:javascript
复制
class 子类名:继承方式1 基类1,继承方式2 基类2…{
      …
};
继承方式
代码语言:javascript
复制
公有继承:public
保护继承:protected
私有继承:private

继承方式

基类public成员

基类protected成员

基类private成员

public

public

protected

private

protected

protected

protected

private

private

private

private

private

这张表格里面公有继承,基类的访问控制权限不变; 保护继承,基类的访问控制权限最低为protected; 私有继承,基类的访问控制权限全部为private.

公有继承特点
  • 子类可以直接访问基类的所有公有和保护成员,其效果如同它们是在子类中声明一样
  • 对于基类的私有成员,在子类中存在但不能访问
  • 在子类中定义基类中同名的公有成员或保护成员,子类中的成员会隐藏基类同名成员,想访问被隐藏的成员,可以借助作用域限定符“::”
  • 子类对象任何时候都可以被当成基类类型对象(皆然性 ISA)
保护继承特点
  • 使基类公有成员和保护成员进行保护化,只禁止外部通过该子类访问
  • 子类指针或引用不能隐式转换成基类类型指针或引用
私有继承特点
  • 将基类公有和保护成员私有化,禁止外部通过该子类访问,也禁止该子类的子类访问
  • 子类指针或引用不能隐式转换成基类类型指针或引用
访问控制权限

访问控制限定符

访问控制属性

基类

子类

外部

友元

public

公有成员

OK

OK

OK

OK

protected

保护成员

OK

OK

NO

OK

private

私有成员

OK

NO

NO

OK

  • 不写继承方式,默认私有继承
  • 继承方式是用来指明基类成员在派生类中的最高访问权限的
  • 使用 using关键字可以改变基类公有和保护成员在派生类中的访问权限
继承演示
代码语言:javascript
复制
#include <string>
#include <iostream>
using namespace std;
class Human
{
public:
	Human()
	{
		cout << "Human  构造:" << this << endl;
	}
	void sleep(){
		cout << "基类 Sleep" << endl;
	}
private:
	int m_age;
protected:
	string m_name;
public:
	bool m_gender;  //0 女  1 男
	int m_show;
};  //基类 父类

class Student : public Human
{
public:
	Student() : m_id(0)
	{
		/*
		*	1.派生类可以直接访问基类的所有公有和保护成员,其效果如同它们是在派生类中声明一样
		*/

		//基类中的保护成员在派生类中可以访问
		m_name = "梦凡";
		//基类中的公有成员在派生类中可以访问
		m_gender = 1;

		/*
		*	2.对于基类的私有成员,在派生类中存在但不能访问
		*/
		//m_age = 10;
	}
	void show()
	{
		//隐藏基类中的m_show变量		基类中的m_show存在,用作用域限定符访问
		cout << "派生类中的 m_show:" << m_show <<" "<< "基类中的m_show:" << Human::m_show << endl;
	}
private:
	int m_id;
public:
	//	3.同名隐藏  派生类中定义基类中同名的公有成员或保护成员(成员包括成员变量和成员函数)
	int m_show;		//隐藏基类中的m_show变量		基类中的m_show存在,用作用域限定符访问
	int sleep;		//隐藏基类中的sleep()函数
};	//派生类 子类

int main()
{
	Student s;
	/*
	*	3.同名隐藏测试
	*/
	//s.sleep("水果");	//会报错 因为派生类中的sleep变量将父类中的sleep函数隐藏了
	s.m_show = 10;			//m_show为派生类中的m_show
	s.Human::m_show = 20;	//通过作用域访问基类中的m_show
	s.show();

	/*
	*	4.公有继承	派生类对象任何时候都可以被当成基类类型对象
	*				基类对象不能隐式转换成派生类对象
	*/
	//缩小访问范围 安全
	Human h = s;
	h.sleep();		//不报错,因为派生类对象隐式转换成基类对象

	Human* ph = &s;	//基类指针指向派生类对象	ph隐式指向基类部分
	Human& rh = s;	//基类引用指向派生类对象	rh隐式指向基类部分

	cin.get();
	return 0;
}
阻断继承

将基类构造函数设为私有,子类永远无法被实例化对象,实现阻断继承

代码语言:javascript
复制
//阻断继承 将构造函数声明为私有
class parent
{
private:
	parent() {}
};
class son :public parent
{
};
int main()
{
    //son s;	无法实例化对象
}
构造析构顺序
子类构造
  • 子类构造函数会调用基类构造函数,构造子类对象中的基类子对象
  • 子类构造函数没有显示指明基类构造方式,会选择基类的缺省构造函数(无参构造)
  • 子类显示调用基类构造函数可以在初始化表中显示指明构造方式。
  • 构造过程:构造基类子对象-》构造成员变量-》执行构造代码
子类析构
  • 子类析构会调用基类析构
  • 通过基类指针析构子类对象,只会析构子类对象中的基类子对象。可能造成内存泄漏
  • 析构过程:执行析构代码-》析构成员变量-》析构基类子对象
演示
代码语言:javascript
复制
#include <string>
#include <iostream>
using namespace std;
class Human
{
public:
	Human()
	{
		cout << "Human构造 this:" << this << endl;
	}
	Human(int age)
	{
		cout << "Human单参 this:" << this << endl;
	}
private:
	int m_age;
	string m_name;
	int m_gender;
};  //基类

class Student : public Human
{
public:
	//默认调用基类无参构造
	//Student() :m_no(0) {
	//	cout << "Student构造 this:" << this << endl;
	//}

	/*
	*	2.通过初始化列表选择基类的构造方式
	*/
	Student(int age) :Human(age), m_no(0)
	{
		std::cout << "Student单参构造 this:" << this << std::endl;
	}
private:
	int m_no;
};

int main()
{
	/*
	*	1.构造顺序	在构造析构里面分析过
	*	为整个对象分配内存
	*	构造基类部分(如果存在基类)
	*	构造成员变量
	*	执行构造函数代码
	*/
	//无参构造
	//Student s1;
	//单参构造
	Student s1(18);
	cin.get();
	return 0;
}
多继承

一个类可以从多个基类继承

多重继承内存布局

子类对象中的基类子对象按照继承表顺序依次构造

代码语言:javascript
复制
#include <iostream>
using namespace std;
class Telephone
{
public:
	Telephone()
	{
		cout << "Telephone构造" << endl;
	}
	void call()
	{
		cout << "打电话" << endl;
	}
	int m_t;
};

class Camera
{
public:
	Camera()
	{
		cout << "Camera构造" << endl;
	}
	void photo()
	{
		cout << "照相" << endl;
	}
	int m_c;
};

class Ipod
{
public:
	Ipod()
	{
		cout << "Ipod构造" << endl;
	}
	void music()
	{
		cout << "听歌" << endl;
	}
	int m_i;
};

//子类对象中的基类子对象按照继承表顺序依次构造
class IphoneXs :public Telephone, public Camera, public Ipod
{
public:
	IphoneXs()
	{
		cout << "IphoneXs构造" << endl;
	}
	int i;
};

int main()
{
	IphoneXs ipx;   //构造一个子类对象
	ipx.call();
	ipx.photo();
	ipx.music();
	cout << "------------------" << endl;

	//基类指针指向子类对象
	Telephone* t = &ipx;  //公有继承 皆然性   子类IS A父类
	Camera* c = &ipx;      //公有继承 皆然性   子类IS A父类
	Ipod* i = &ipx;		  //公有继承 皆然性   子类IS A父类

	cout << t << " " << c << " " << i << endl;
	cin.get();
	return 0;
}
代码语言:javascript
复制
Telephone构造
Camera构造
Ipod构造
IphoneXs构造
打电话
照相
听歌
------------------
006FF754 006FF758 006FF75C
棱形继承

中间子类有公共基类,导致最终子类存在多个公共基类,导致调用歧义通过使用::明确调用

代码语言:javascript
复制
#include <iostream>
using namespace std;
class A
{
public:
	A()
	{
		cout << "A构造" << this << endl;
	}
	int m_a;
	void foo()
	{
		m_a = 10;
		cout << m_a << endl;
	}
};	

class X :public A
{
public:
	X()
	{
		cout << "X构造" << this << endl;
	}
};

class Y :public A
{
public:
	Y()
	{
		cout << "Y构造" << this << endl;
	}
};

class Z :public X, public Y
{
public:
	Z()
	{
		cout << "Z构造" << this << endl;
	}
};

int main()
{
	Z z;
	//z.foo();   调用不明确   z对象中有X和Y的对象,X和Y中有A的对象
	//不知道调用X->A.foo 还是调用Y->A.foo();
	z.X::foo();
	z.Y::foo();

	//z.A::foo();
	z.X::A::foo();
	return 0;
}
虚继承

在公共基类继承方式前加关键字virtual 在公共基类对象保存到公共位置,存储偏移值

虚继承是为了解决棱形继承中成员访问的二义性。在A B继承方式前加关键字virtual,编译器将Base的数据保存在一个公共位置,通过虚基表访问。

代码语言:javascript
复制
#include <iostream>
using namespace std;
class A
{
public:
	A()
	{
		cout << "A构造" << this << endl;
	}
	int m_a;
	int m_b;
	void foo()
	{
		m_a = 10;
		cout << m_a << endl;
	}
};
/*
*	解决方法二:虚继承基类A
*/
class X :virtual public A
{
public:
	X()
	{
		cout << "X构造" << this << endl;
	}
};

class Y :virtual public A
{
public:
	Y()
	{
		cout << "Y构造" << this << endl;
	}
};

class Z :public X, public Y
{
public:
	Z()
	{
		cout << "Z构造" << this << endl;
	}
};

int main()
{
    cout << "A:" << sizeof(A) << ",X:" << sizeof(X) << ",Y:" << sizeof(Y) << ",Z:" << sizeof(Z) << endl;
	Z z;
	//z.foo();	//对“foo”的访问不明确	
	/*
	*	解决方法一:作用域::这么调用哪个父类里面的foo函数
	*/
	//z.X::foo();
	//z.Y::foo();
	z.foo();	//虚继承解决棱形继承问题
	cin.get();
	return 0;
}
代码语言:javascript
复制
A:4,X:8,Y:8,Z:12
A构造00F6F994
X构造00F6F98C
Y构造00F6F990
Z构造00F6F98C
10

End

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程学习基地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 继承
    • 继承方式
      • 公有继承特点
      • 保护继承特点
      • 私有继承特点
    • 访问控制权限
      • 继承演示
        • 阻断继承
          • 构造析构顺序
            • 子类构造
            • 子类析构
            • 演示
          • 多继承
            • 多重继承内存布局
          • 棱形继承
            • 虚继承
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档