C++编程——继承


继承的知识点结构
在这里插入图片描述
继承是面向对象三大特性之一,例如动物类下可以有猫类、狗类。显然猫类、狗类都具有动物类的属性,于此同时,也就有自己独特的属性。这个时候,猫类、狗类就是继承于动物类, 采用继承的技术,可以减少重复的代码

1. 继承的基本语法

语法class 子类:继承方式 基类
继承示例
一般网页都有很多部分相似的,我们可以将这些相似的部分抽象出来,作为一个基类。然后子类只需要继承基类,在基类的基础上添加自身其他的属性或者方法就可以实现基本的功能,同时减少了重复代码。

//继承实现页面
//公共页面类
class BasePage
{
public:
	void header()
	{
		cout << "网页公共头部信息" << endl;
	}
	void footer()
	{
		cout << "网页公共底部信息" << endl;
	}
	void left()
	{
		cout << "网页公共分类列表信息" << endl;
	}
};


//Java页面
class Java :public BasePage
{
public:
	void content()
	{
		cout << "Java网页信息" << endl;
	}
};


//Python页面
class Python :public BasePage
{
public:
	void content()
	{
		cout << "Python网页信息" << endl;
	}
};


//CPP页面
class CPP :public BasePage
{
public:
	void content()
	{
		cout << "CPP网页信息" << endl;
	}
};

void test01()
{
	cout << "Java页面如下" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();

	cout << "Python页面如下" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();

	cout << "CPP页面如下" << endl;
	CPP cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}


int main()	
{
	test01();
	system("pause");
	return 0;
}

2. 继承方式

继承方式一共有三种:

  • 公共继承public
  • 保护继承protected
  • 私有继承private

:保护访问权限和私有访问权限都是类内可访问,类外不能访问。但是两者的不同点会在继承中体现。

下面将说明发生继承后,子类访问权限的变化情况:
基类

class A
{
public:
	int a;
protected:
	int b;
private:
	int c;
};
  • 公有方式继承基类
class B :public A
{
public:
	int a;
protected:
	int b;
不可访问:
	int c:
};
  • 保护方式继承基类
class B :protected A
{
protected:
	int a;
	int b;
不可访问:
	int c:
};
  • 私有方式继承基类
class B :private A
{
private:
	int a;
	int b;
不可访问:
	int c:
};

3. 继承的对象模型

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

先来看一段代码:

class Base
{
public: 
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son : public Base
{
public:
	int m_D;
};

void test01()
{
	cout << "size of Son = " << sizeof(Son) << endl;
}


int main()	
{
	test01();
	system("pause");
	return 0;
}

输出的结果如下:
在这里插入图片描述
可以得到以下结论:

  1. 父类中所有非静态成员属性都会被子类继承下去
  2. 父类中私有成员属性是被编译器给隐藏了,因此访问不到,但是确实是被继承下去了

利用开发工具验证继承的对象模型

  1. 打开Visual Studio的开发人员命令提示符
    在这里插入图片描述
    2.将路径定位到所要查看类对象模型所在文件夹的路径
    在这里插入图片描述
    3.输入代码:cl(l代表字母l) /d1(1代表数字1) reportSingleClassLayout类名 类所在的文件名,并得到结果
    在这里插入图片描述

4. 继承中构造和析构顺序

示例

//继承中的构造和析构顺序
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 test01()
{
	Son son;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述继承中的构造和析构顺序如下
先构造父类,再构造子类,析构的顺序与构造的顺序相反

5. 继承中同名成员处理

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

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

示例

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

	int m_A;
};


class Son :public Base
{
public:
	Son()
	{
		m_A = 200;
	}

	int m_A;
};


void test01()
{
	Son son;
	cout << "子类中的m_A = " << son.m_A << endl;
	cout << "父类中的m_A = " << son.Base::m_A << endl;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述
:同名成员函数的处理和同名成员属性的处理相同,要注意的是:如果子类中出现和父类同名的成员函数,子类的同名成员函数会隐藏掉父类中所有同名成员函数

6. 继承中同名静态成员处理方式

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

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

6.1 通过对象访问

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 test01()
{
	Son s;
	cout << "子类中的静态成员m_A = " << s.m_A << endl;
	cout << "父类中的静态成员m_A = " << s.Base::m_A << endl;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

6.2 通过类名访问

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 test01()
{
	
	cout << "子类中的静态成员m_A = " << Son::m_A << endl;
	cout << "父类中的静态成员m_A = " << Son::Base::m_A << endl;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

7. 多继承语法

C++允许一个类继承多个类
语法:class 子类: 继承方式 父类1, 继承方式 父类2 ...
:多继承可能会引发父类中有同名成员出现,需要加作用域区分,C++的实际开发中不建议用多继承的技术

7.1 多继承的对象模型

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



class Base2
{
public:
	Base2()
	{
		m_A = 200;
	}
	int m_A = 200;
};

class Son :public Base1,public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};



void test01()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl;
	cout << "Base1中的m_A = " << s.Base1::m_A << endl;
	cout << "Base2中的m_A = " << s.Base2::m_A << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

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

也可以利用开发者工具查看模型:
在这里插入图片描述

8. 菱形继承(钻石继承)

概念:两个派生类继承同一个基类,又有某个类同时继承着两个派生类

典型的菱形继承案例
动物作为基类,羊类和驼类作为动物的派生类,羊驼类同时继承羊类和驼类

菱形继承问题

  1. 羊继承了动物的数据,驼也同样继承了动物的数据,当羊驼使用数据时,就会产生二义性。
//动物类
class Animal
{
public:
	int m_Age;
};

//羊类
class Sheep: public Animal
{

};

//驼类
class Camel : public Animal
{

};

//羊驼类
class SheepCamel: public Sheep, public Camel
{
	
};


void test01()
{
	SheepCamel sc;
	sc.m_Age = 18; //这行代码就会报错,因此羊驼类继承了两份m_Age,直接调用sc.m_Age产生了二义性
	sc.Sheep::m_Age = 18;
	sc.Camel::m_Age = 20; //可以利用添加作用域的方式来解决二义性的问题
}


int main()
{
	test01();
	system("pause");
	return 0;
}
  1. 羊驼类实际上继承两份动物的数据,造成了内存的浪费(例如羊有年龄,驼也有年龄,羊驼都继承了羊类和驼类关于年龄的数据,但实际上羊驼的年龄只有一个,没有必要拥有两份关于年龄的数据)

可以参考多继承的对象模型:
在这里插入图片描述

采用虚基类解决菱形继承的问题
继承之前,加上关键字virtual变为虚继承

//动物类
class Animal
{
public:
	int m_Age;
};

//羊类
class Sheep:virtual public Animal
{

};

//驼类
class Camel :virtual public Animal
{

};

//羊驼类
class SheepCamel: public Sheep, public Camel
{
	
};


void test01()
{
	SheepCamel sc;
	sc.Sheep::m_Age = 18;
	sc.Camel::m_Age = 28;
	 
	cout << "sc.Sheep::m_Age = " << sc.Sheep::m_Age << endl;
	cout << "sc.Camel::m_Age = " << sc.Camel::m_Age << endl;
	cout << "sc.m_Age = " << sc.m_Age << endl;
}


int main()
{
	test01();
	system("pause");
	return 0;
}

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

说明:采用虚继承之后,菱形继承中羊驼类就只有一份年龄的数据了,解决了内存浪费的问题

虚继承的底层原理
我们利用开发者工具调出羊驼类的对象模型:
在这里插入图片描述
和未采用虚继承技术的对象模型相比,年龄的数据变成只有一份了,但是多了vbptr
vbptr称为虚基类指针,指针指向的是vbtable,也就是虚基类表,虚基类表中的内容其实就是一个偏移量

  • Sheep中的虚基类指针指向的虚基类表中的偏移量为8,(0+8=8)就找到了年龄数据的地址
  • Camel中的虚基类指针指向的虚基类表中的偏移量为4,(4+4=8)就找到了年龄数据的地址
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值