【c++】——继承和派生


我们都知道继承是oop语言的一种强大机制,我们还知道类和类之间的关系有两种
组合:a part of…一部分的关系
继承:a kind of…一种的关系。
这篇文章,我们就来好好的谈一谈它吧~

一、继承

1、继承的本质和原理

继承的本质其实就是代码的复用。如下代码,B继承A

class A
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;//只有自己或者友元能访问私有的成员
};
class B : A
{
public:
	int md;
	void fun()
	{
		cout << ma << endl;
	}
protected:
	int me;
private:
	int mf;
};

A,B类之间的关系如图所示:
在这里插入图片描述
用继承的方式复用A(基类、父类)里面的ma,mb,mc。继承的过程还附带了作用域。

2、继承方式

B 类继承A类有三种继承方式,对于类里面的访问限定,有如下图所示的对应方式:
在这里插入图片描述
由此,我们可以总结出继承方式如下:

  1. 外部只能访问对象public的成员,protected和private的成员无法直接访问
  2. 在继承结构中,派生类从基类可以继承过来private的成员,但是派生类却无法直接访问
  3. protected和private的区别——在基类中,定义的成员,想被派生类访问但是不想被外部访问,那么在基类中,把相关成员定义成protected
    如果派生类和外部都不打算访问,那么在基类中,就把相关成员定义成private私有的

默认的继承方式:
要看派生类使用class定义的,还是struct定义的.
class定义派生类,默认继承方式就是private私有的,class定义派生类,默认继承方式就是public共有的

3、继承结构中,从上(基类)到下(派生类)的转换

还是上述代码,我们为其增添了show函数加以改造如下:

class Base
{
public:
	Base(int data = 10) :ma(data) { }
	void show() { cout << "Base::show()" << endl; }//1
	void show(int) { cout << "Base::show(int)" << endl; }//2
protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20)
		:Base(data), mb(data){}
	void show() { cout << "Derive::show()" << endl; }//3
private:
	int mb;
};

接下来,我们要利用上述代码来探究一下以下四种转换
基类对象到派生类对象的转换
派生类对象到基类对象的转换
基类指针(引用)到派生类对象
派生类指针(引用)到基类对象

(1)基类对象b<- 派生对象d

int main()
{
	Base b(10);
	Derive d(20);
	b = d;
	return 0;
}

这种方式就相当于,需要一个人,给你一个老师,是一个类型从下到上的转换,是可以的

(2)派生对象d <- 基类对象b

int main()
{
	Base b(10);
	Derive d(20);
	d = b;
	return 0;
}

这种方式相当于,一个学生做高等数学,但是随便一个人是不可能完成的,是一个类型从上到下的过程是不可以的

(3)基类指针(引用)<- 派生类对象

int main()
{
	Base b(10);
	Derive d(20);
	Base* pb = &d;
	return 0;
}

这种方式是一个类型从下到上的转换,是可以的。 但是指针的类型只是个基类,限制了只能访问派生类中的基类部分的成员

若想访问派生类成员,就要做类型强转,如下:

((Derive*)pb)->show();

(4)派生类指针(引用)<- 基类对象

int main()
{
	Base b(10);
	Derive d(20);
	Derive* pb = &b;
	return 0;
}

这种方式是不可以的,是一个类型从上到下的过程。
以下方式可以解决问题,但是不安全,涉及了内存的非法访问

总结: 在继承结构中进行上下的类型转换,默认只支持从下到上的类型的转换

二、派生

1、派生类怎么初始化从基类继承来的成员变量?

在回答这个问题之前,我们首先来实现一个继承的代码,其中就包含了构造函数的初始化。

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data)
		:ma(data), mb(data) 
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;
};
int main()
{
	Derive d(20);
	return 0;
}

但是仔细观察上述代码我,我们会发现其派生类对象构造函数的初始化是错误的。因为基类没有合适的默认构造函数可用 因此,基类的成员不能在派生类里面进行初始化。

所以,我们应该将派生类对象构造函数的初始化过程改成如下方式:

Derive(int data)
		:Base(data), mb(data)
	{
		cout << "Derive()" << endl;
	}

通过调用基类相应的构造函数来初始化.
派生类从基类可以继承来所有成员(变量和方法),除去构造函数和析构函数(因为这两只在当前类里有意义)
派生类的构造和析构函数负责初始化和清理派生类部分,派生类从基类继承来的成员的初始化和清理由基类的构造和析构负责

2、派生类对象构造和析构的过程

  1. 派生类调用基类的构造函数,初始化从积累继承来的成员

  2. 调用派生类自己的构造函数,初始化派生类自己特有的成员

…派生类对象的作用域到期了

  1. 调用派生类的析构函数,释放派生类成员可能占用的外部资源(堆内存,文件)
  2. 调用基类的析构函数,释放派生类内存中,从基类继承来的成员可能占用的外部资源(堆内存,文件)

如下图:

在这里插入图片描述

3、派生类作用域的隐藏

还记得上面我们所提到的继承结构中,从上(基类)到下(派生类)的转换这个知识点的代码吗?
如果在main函数里面,如下方式使用show函数就会出错

int main()
{
	Derive d;
	d.show();
	d.show(10);
	return 0;
}

会出现"Derive::show()"函数不接受1个参数这个错误,原因是派生类show对基类的show作用域隐藏了,优先寻找派生类自己作用域的show名字成员,没有的才去基类里面找,可以使用以下的方式改正

d.Base::show(10);

所以,可以得出,在继承机构中,派生类的同名成员把基类的同名成员隐藏了。这种隐藏是一种作用域隐藏的关系。

发布了98 篇原创文章 · 获赞 9 · 访问量 3665

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105195932