C++继承----详解


前言

环境:VS2019版本!!!

class Dog
{
public :
	Dog(string name, string sex, int age) :_name(name),_sex(sex),_age(_age)
	{
	}
	void Eat()
	{
		cout << _name << "在吃东西" << endl;
	}
	void Sleep()
	{
		cout << _name << "在睡觉" << endl;
	}
	void Run()
	{
		cout << _name << "在跑" << endl;
	}
	string _name;
	int _age;
	string _sex;
};
class Cat
{
public:
	Cat(string name, string sex, int age) :_name(name), _sex(sex), _age(_age)
	{

	}
	void Eat()
	{
		cout << _name << "在吃东西" << endl;
	}
	void Sleep()
	{
		cout << _name << "在睡觉" << endl;
	}
	void Run()
	{
		cout << _name << "在跑" << endl;
	}
	string _name;
	int _age;
	string _sex;
};

细心的铁铁,一定发现了在以上Dog和Cat两个类,都有写Eat,Sleep,Run方法和相同的属性
使得代码出现了极大冗余性。若在增添一个小猪类,结果可想而知!!
继承则大大解决了这个问题!!!

1、继承的概念及定义

1.1继承的概念

继承机制是面向对象程序设计是代码可以复用的最重要手段,它允许程序员在保持原有类特性的基础上进行扩展(即增加新的类特有的功能及属性),这样产生的新类,称作派生类
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,继承是类设计层次的复用


1.2 继承的定义

1.2.1定义格式

下面我们看到的Animal是父类,也称作基类。Dog类是子类,也称做派生类
在这里插入图片描述

1.2.2继承关系和访问限定符

在这里插入图片描述

1.2.3继承基类成员访问方式的变化

在这里插入图片描述

注意:
1.基类私有成员,对子类对象不可见,但是此私有成员都同公有和保护的成员被继承下来了

2.使用关键字class时默认继承方式为private,关键struct时默认继承方式为public,故在继承环境下,应当显示提供继承方式

2、基类和派生类对象赋值关系

提示:
在这里插入图片描述

在public继承关系下,对象赋值关系,又名赋值兼容规则


class Animal
{
public:
	string _name;
	int _age;
	string _sex;
};
class Dog:public Animal
{
public :
	string _color;
};

a.用子类对象给基类对象赋值
在这里插入图片描述
b.用基类对象指针指向子类对象
c.用基类对象引用引用子类对象
在这里插入图片描述

3、继承中的作用域

注: 在继承体系中,基类和子类都有独立的作用域,即两者作用域不相同

3.1 同名隐藏||重定义

同名隐藏,又名重定义,意在不同作用域下(即继承中的基类和子类)存在相同名称的成员,子类成员将屏蔽基类对同名成员的访问

在这里插入图片描述

//
//
//A中的变量_a和函数f1()与B中的相同名称成员构成了隐藏
class A {
public:
	void f1()
	{
		cout << "A::f1()" << endl;
	}
	int _a;
};
class B :public A
{
public:
	void f1(double a)
	{
		cout << "B::f1()" << endl;
	}
	double _a;
};

注意:
1.子类对象只能访问本类的相同名称成员,不能访问基类及基类以上的继承的同名变量
2.若要访问基类,则需加访问限定符

class A {
public:
	
	void f1(int a)
	{
		_a = a;
	}
	int _a;
};
class B :public A
{
public:
	
	void f1(int a,int b)
	{
		_a = a;
		_b = b;
	}
	int _b;
};

在这里插入图片描述

4.派生类的默认成员函数

1.构造函数(普通继承下)
A.基类构造方法(没有显示提供/无参/全缺省),子类的构造方法可不提供
B.基类的构造方法非默认且带参数,子类的构造方法必须提供,且子类构造方法初始化列表需要显式调用基类的构造方法
在这里插入图片描述

C.未完待续…
2.拷贝构造函数
A.当涉及资源管理是,子类基类的拷贝构造函数都需要显示提供,否则会发生浅拷贝,反之则不显示提供。

class A {
public:
	A(int a = 0) :_a(a)
	{
	}
	A(const A& aa):_a(aa._a)
	{

	}
	int _a;
};
class B :public A
{
public:
	B(int a,int b):A(a),_b(b)
	{
		arr = new int [10];
	}
	~B()
	{
		delete[] arr;
	}
	//B(const B& bb) :A(bb), _b(bb._b)
	//{
	//   arr=new int[10]
	//}
	int _b;
	int* arr;

};

在这里插入图片描述
变量boy和boy1的arr变量指向同一份空间发生了浅拷贝


B.子类间的赋值分为基类部分成员变量的赋值子类新增部分变量赋值(但涉及基类部分成员的赋值可采用,访问限定符的方式)
在这里插入图片描述

class A {
public:
	A(int a = 0) :_a(a)
	{
	}
	A(const A& aa):_a(aa._a)
	{

	}
	A& operator=(const A& aa)
	{
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	 }
	int _a;
};
class B :public A
{
public:
	B(int a,int b):A(a),_b(b)
	{
		arr = new int [10];
	}
	~B()
	{
		delete[] arr;
	}
	B(const B& bb) :A(bb), _b(bb._b)
	{
		arr = new int[10];
	}
	B& operator=(const B& bb)
	{
		if (this != &bb)
		{
			//用bb中A的部分给*this中A的部分赋值
			//*this = bb;此方法会陷入该赋值运算符重载方法的无限递归
			A::operator=(bb);
			//子类新增部分赋值
			_b = bb._b;
			delete[] arr;
			arr = new int[10];
			//bb.arr空间中的内容拷贝刀arr中
			for(int i=0;i<d.arr.size();i++)
			{
				arr[i]=bb.arr[i];
			}
		}
	}
public:
	int _b;
	int* arr;

};

3.析构函数

class A {
public:
	A()
	{
		cout << "A::A()" << endl;
	}
	~A()
	{
		cout << "A::~A()" << endl;
	}
};
class B :public A
{
public:
	B()
	{
		cout << "B::B()" << endl;
	}
	~B()
	{
		cout << "B::~B()" << endl;
	}
};
int main()
{
	B();
	return 0;
}

在这里插入图片描述
该代码中,在对B类析构时,会先调用~B(),在打印完成之后,编译器会自动调用一条汇编指令,
call A::~A(),即调用基类的析构函数,对从基类继承下来的部分的成员进行销毁。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值