c++基础 面向对象:第八篇(继承)

继承的基本概念

在之前的学习中,我们了解了类中的属性和行为的关系,这次我们谈一谈类与类之间的关系。
在对于一些类中,其实有一些共同的特性的:如下图,动物类分为猫类,狗类,猫类又可以分为好多种品种的猫,狗又可以分为很多品种的狗,但归结起来他们都是动物类,这说明类与类之间存在着一种继承的关系,下面的小类,继承了上面的大类的特性。着说明小类有自己的特性的同时,还拥有上面大类的共性。
在这里插入图片描述
在C++中提供了一种继承的技术,让我们能减少重复代码的书写。


继承的好处

如何减少重复代码量呢?举个猫类的例子:
假如我们设计一个
黄猫类,这个猫,会叫,会走,会吃;特别与其他猫,它是黄色的;
白猫类:这个猫,会叫,会走,会吃;特别与其他猫,它是白色的;
黑猫类:这个猫,会叫,会走,会吃;特别与其他猫,它是黑色的;
用代码实现的是:(没用继承的方式)

class YellowCat //黄猫类
{
public:
	void Speak()
	{
		cout << "Speak()的调用" << endl;
	}
	void Go()
	{
		cout << "Go()的调用" << endl;
	}
	void Eat()
	{
		cout << "Eat()的调用" << endl;
	}
	void Colour()
	{
		cout << "我是黄色的猫" << endl;
	}
	
};
class WhiteCat  //白猫类
{
public:
	void Speak()
	{
		cout << "Speak()的调用" << endl;
	}
	void Go()
	{
		cout << "Go()的调用" << endl;
	}
	void Eat()
	{
		cout << "Eat()的调用" << endl;
	}
	void Colour()
	{
		cout << "我是白色的猫" << endl;
	}

};
class BlackCat //黑猫类
{
public:
	void Speak()
	{
		cout << "Speak()的调用" << endl;
	}
	void Go()
	{
		cout << "Go()的调用" << endl;
	}
	void Eat()
	{
		cout << "Eat()的调用" << endl;
	}
	void Colour()
	{
		cout << "我是黑色的的猫" << endl;
	}
};
void test01()
{	//创建黄猫对象,调用里面的函数
	YellowCat yellowCat;
	cout << "下面的是黄色的猫:" << endl<< endl;
	yellowCat.Speak();
	yellowCat.Go();
	yellowCat.Eat();
	yellowCat.Colour();
	cout << endl << "-----------" << endl;
}
void test02()
{	//创建白猫对象,调用里面的函数
	WhiteCat whiteCat;
	cout << "下面的是白色的猫:" << endl << endl;
	whiteCat.Speak();
	whiteCat.Go();
	whiteCat.Eat();
	whiteCat.Colour();
	cout << endl << "-----------" << endl;
}

void test03()
{
	//创建黑猫对象,调用里面的函数
	WhiteCat BlackCat;
	cout << "下面的是黑猫的猫:" << endl << endl;
	BlackCat.Speak();
	BlackCat.Go();
	BlackCat.Eat();
	BlackCat.Colour();
	cout << endl << "-----------" << endl;

}
int main(){

	test01();

	test02();
	
	test03();

	system("pause");
	return 0;
}

测试结果:
在这里插入图片描述
重点观察黄猫和白猫和黑猫的类,里面又公共的类型:Speak,Go,Eat;就 Color不一样,所以呢这三个类都是由重复的实现部分,我们可以把重复的单独用一个类实现,然后在其他类继承

语法: class A : 继承方式 B
A 为子类,或派生类
B 为父类,或基类

看下面的继承方式

class Cat //父类,基类
{
public:
	void Speak()
	{
		cout << "Speak()的调用" << endl;
	}
	void Go()
	{
		cout << "Go()的调用" << endl;
	}
	void Eat()
	{
		cout << "Eat()的调用" << endl;
	}
};

class YellowCat : public Cat //黄猫类继承猫类
{
public:
	void Colour()
	{
		cout << "我是黄色的猫" << endl;
	}
	
};
class WhiteCat :public Cat //白猫类继承猫类
{
public:
	void Colour()
	{
		cout << "我是白色的猫" << endl;
	}

};
class BlackCat:public Cat //黑猫类继承猫类
{
public:
	void Colour()
	{
		cout << "我是黑色的的猫" << endl;
	}
};
void test01()
{	//创建黄猫对象,调用里面的函数
	YellowCat yellowCat;
	cout << "下面的是黄色的猫:" << endl<< endl;
	yellowCat.Speak();
	yellowCat.Go();
	yellowCat.Eat();
	yellowCat.Colour();
	cout << endl << "-----------" << endl;
}
void test02()
{	//创建白猫对象,调用里面的函数
	WhiteCat whiteCat;
	cout << "下面的是白色的猫:" << endl << endl;
	whiteCat.Speak();
	whiteCat.Go();
	whiteCat.Eat();
	whiteCat.Colour();
	cout << endl << "-----------" << endl;
}

void test03()
{
	//创建黑猫对象,调用里面的函数
	WhiteCat BlackCat;
	cout << "下面的是黑猫的猫:" << endl << endl;
	BlackCat.Speak();
	BlackCat.Go();
	BlackCat.Eat();
	BlackCat.Colour();
	cout << endl << "-----------" << endl;

}
int main(){

	test01();

	test02();
	
	test03();

	system("pause");
	return 0;
}

结果与普通的方式是一样的,但是在代码上却上了一大堆重复的代码。


继承方式

在c++中,继承方式和成员属性类似,都有三种权限:
共有继承:子类不可以访问父类的私有属性;在子类中继承父类的共有和保护权限不变;
保护继承:子类不可以访问父类的私有属性;在子类中继承父类的共有权限变保护权限;
私有继承:子类不可以访问父类的私有属性;在子类中继承父类的共有和保护都变成私有权限;
在这里插入图片描述
代码验证:共有继承方式

class Base //父类
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

class Son1 : public Base //共有继承方式
{
public:
	void fun()
	{
		m_a = 10; //父类的共有成员,在子类中可以被访问
		m_b = 20; //父类的保护成员,在子类中可以被访问
		m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
	}
	
};
//类外测试:子类中的对象,共有继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
	Son1 son1;
	son1.m_a = 10; //类外可以访问父类中的共有成员属性;
	son1.m_b = 20; //错误,类外不可以访问父类的保护成员属性;说明共有继承父类时候,在子类中继承父类的保护权限没有发生改变
	son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码测试:保护继承方式

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

class Son1 : protected Base //共有继承方式
{
public:
	void fun()
	{
		m_a = 10; //父类的共有成员,在子类中可以被访问
		m_b = 20; //父类的保护成员,在子类中可以被访问
		m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
	}
	
};
//类外测试:子类中的对象,保护继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
	Son1 son1;
	son1.m_a = 10; //错误,保护继承父类时,在子类中,父类的共有变成了保护权限;
	son1.m_b = 20; //错误,类外不可以访问父类的保护成员属性;说明保护继承父类时候,在子类中继承父类的保护权限没有发生改变
	son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}

在这里插入图片描述
代码验证:私有继承方式

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

class Son1 : private Base //共有继承方式
{
public:
	void fun()
	{
		m_a = 10; //父类的共有成员,在子类中可以被访问
		m_b = 20; //父类的保护成员,在子类中可以被访问
		m_c = 30; //错误,父类的私有成员,在子类中不可以被访问
	}
	
};
//类外测试:子类中的对象,保护继承了父类的属性,测试,对子类对象访问的权限
void test01()
{
	Son1 son1;
	son1.m_a = 10; //错误,私有继承父类时,在子类中,父类的共有变成了私有权限;
	son1.m_b = 20; //错误,私有继承父类时候,在子类中继承父类的保护权限改变成为私有权限
	son1.m_c = 30; //错误,类外不可以访问父类的私有成员属性;
}

在这里插入图片描述

继承中的对象模型大小

观察以下代码:问 Son1类继承了Base类,问 对象son的字节数是多少?

lass Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};

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

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

在这里插入图片描述

在这里插入图片描述
其结果16个字节,说明父类中的private中的属性也被继承了,只是被编译器隐藏了。


继承中的构造和析构顺序

其实构造和析构顺序之前在讲:类对象作为另一个类中的成员属性时候讲过;
都是先构造里面的,再构造外面的,析构和构造相反;
也就是说,先构造父类,再构造子类,析构和构造相反;

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 s;
}

int main() {

	test01();

	system("pause");

	return 0;
}

在这里插入图片描述

继承中同名成员的处理

假如继承中,子类和父类的成员同名,在创建子类对象时候,调用同名的成员时候,会输出什么呢?
假如我要通过子类对象访问父类的同名成员属性,如何访问呢?在调用时候加个父类的作用域就行。
当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数.

class Base
{
public:
	int m_a;
	Base()
	{
		m_a = 100;
		cout<<"Base类的成员属性调用"<<endl;
	}
	void fun()
	{
		cout <<"Base类的成员函数的调用"<<endl;
	}
};

class Son :public Base
{
public:
	int m_a;
	Son()
	{
		m_a = 200;
	}
	void fun()
	{
			cout<<"Son类的成员函数调用"<<endl;
	}
};
void test01()
{
	Son son;
	cout << son.m_a << endl; //访问的是Son类的m_a;200
	 son.fun() ;//访问的是Son类的fun函数;
	
	cout << son.Base::m_a<<endl; //访问的Base类的m_a;100
	son.Base::fun();//访问的是Base类的fun函数;
	
}

有木有发现,虽然我调用的是 son.m_a;访问的值是200;但是也会调用父类的构造函数,并没有访问其父类Base的m_a的属性
在这里插入图片描述

菱形继承的问题

子类1和子类2继承了一个父类;同时子类3,分别继承了子类1和子类2的问题;
在这里插入图片描述
就很典型的菱形继承问题:羊和驼分别继承了动物类;而草泥马分别继承了羊和驼类;
菱形继承会带来的问题:
会引发二义性;假如我在草泥马中通过羊,而草泥马继承下来羊和驼同时继承了动物,就会继承两份动物的数据,这是一摸一样的,没有必要。
我们可以通过虚继承的方式解决上述问题:可以解决二义性的问题;
下面是没加virtual关键字的;即不是虚继承

class Animal
{
public:
	int m_Age;
};

class Sheep :  public Animal {};
class Tuo   :  public Animal {};
class SheepTuo : public Sheep, public Tuo {};//羊驼继承了羊和驼

void test01()
{
	SheepTuo st;
	//羊驼访问羊里面的 m_A
	st.Sheep::m_Age = 100;
	//羊驼访问羊里面的 m_A
	st.Tuo::m_Age = 200;

	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; //结果 100
	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl; // 结果 200
	
}


int main() {

	test01();

	system("pause");

	return 0;
}

问题来了,上面两个确实可以输出结果,可是我羊驼 到底是 100 呢,还是200 ,所以我们用虚继承方式解决问题,原理就暂时不讲,会使用先。

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 test01()
{
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 200;
	//下面结果全是200
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl; 
	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
	cout << "st.m_Age = " << st.m_Age << endl;
}


int main() {

	test01();

	system("pause");

	return 0;
}

上诉输出的都是200 ,因为用到了菱形继承的虚继承方式,把二义性的问题给解决了,这就使得羊驼的数据都是 一份,没有两份了。

总结

继承的故事就到这里了,继承就是子类继承了父类里面的成员;了解一些常用的用法;是我们的c++基础篇的目的;

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呋喃吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值