C++-----继承那些事儿(一)

善有恒何必三更眠五更起,最无益莫过一日瀑十日寒。每天进步一点点,终会成为梦想的自己。

目录

何为继承

单继承和多继承

派生类中,从基类继承来的成员的访问限定

派生类怎么初始化从基类继承来的成员

基类和派生类同名成员函数之间的关系

赋值兼容性规则


何为继承

C++作为一种面向对象编程语言,其好处之一就是代码的复用。为了实现代码的复用C++采用方式之一就是通过继承。所谓继承就是从已有类得到属性和行为特征。换一角度来说,从已有类产生新类的过程就是派生。其中已有类称为基类或者父类,在它的基础上建立的新类称为派生类或子类。基类一般包含所有类公共的成员,派生在基类的基础上添加一些不同于基类的数据成员以及成员方法。继承分为单继承和多继承,下面我们进行介绍。

单继承和多继承

单继承的一般形式为:

class<派生类名>: <继承方式><基类名>
{  //派生类新增加的数据成员和成员函数};

/*继承方式:共有继承(public)
           私有继承(private)
           保护继承(protected)
*/

下面我们来看单继承的一个具体例子:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :ma(a) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
	void show_Base() { cout << "Base::show()" << endl; }
protected:
	int ma;
};
class Derive : public Base
{
public:
	Derive(int data) :Base(data), mb(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show_Derive() 
	{ 
		cout << "Derive::show()" << endl;
	}
private:
	int mb;
};

上面代码,Base是基类或父类,Derive是派生类或子类,Derive类公有派生Base类,此时Derive类不仅有自己类中所定义的成员函数和数据成员,还有从基类继承过来的基类中的成员方法和数据成员。

多继承的一般形式为:

class<派生类名>: <继承方式1><基类名1>,......,<继承方式n><基类名n>
{  //派生类新增加的数据成员和成员函数};

/*继承方式:共有继承(public)
           私有继承(private)
           保护继承(protected)
*/

我们依旧先举出一个多继承的例子:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a) :ma(a) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
	void show() { cout << "Base::show()" << endl; }
	void show(int i) { cout << "Base::show(int)" << endl; }
protected:
	int ma;
};

class Base1
{
public:
	Base1(int b) :mb(b) { cout << "Base1()" << endl; }
	~Base1() { cout << "~Base1()" << endl; }
	void show1() { cout << "Base1::show1()" << endl; }
private:
	int mb;
};

class Derive : public Base , public Base1
{
public:
	Derive(int data) :Base(data),Base1(data), mc(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show2() 
	{ 
		cout << "Derive::show()" << endl;
	}
private:
	int mc;
};

上面代码,Base类和Base1类都是基类或父类,Derive类是派生类或子类。Derive类公有派生Base类和Base1类,此时Derive类不仅有自己类中所定义的成员函数和数据成员,还有从基类继承过来的基类中的成员方法和数据成员。

派生类中,从基类继承来的成员的访问限定是什么?

在上面的介绍中,我们知道派生类继承基类,在派生类中包含了基类的成员方法和数据成员。那么派生类中,从基类继承过来的数据成员和成员方法我们如何进行使用,其访问限定是什么???

我们以一张表格的形式进行说明:

继承方式: 基类的访问限定 派生类的访问限定 main函数
public: public public yes
  protected protected no
  private 存在不可访问 no
protected: public protected no
  protected protected no
  private 存在不可访问 no
private: public private no
  protected private no
  private 存在不可访问 no

总结:
1.基类的private私有成员,无论采用什么继承方式,在派生类里面都是可以继承,但是无法访问。
2.protected和private的区别。基类的private私有成员,在派生类和外部都不能直接访问;但是基类protected的成员,在派生类中是可以访问的,在外部不能访问。

派生类怎么初始化从基类继承来的成员呢?

通过调用基类相应的构造函数进行初始化,派生类只会初始化自己的成员。

我们知道派生类继承基类,派生类会包含基类的数据成员,我们先修改单继承方式中构造函数中的代码,

Derive(int data) :ma(data), mb(data)  //其中ma为从基类继承过来的数据成员
{ cout << "Derive()" << endl; }

我们试图通过赋值的方式直接给基类的数据成员ma赋值,但是当我们运行的时候会发生错误“找不到合适的构造函数”,原来派生类只会初始化自己的数据成员,并不会初始化从基类继承过来的数据成员,想要初始化从基类继承过来的数据成员,就需要通过基类相应的构造函数进行初始化。

基类和派生类同名成员函数之间的关系

基类和派生类同名函数之间的关系一般有三种:分别是重载,隐藏和覆盖。

重载 重载指函数名相同,形参列表不同,作用域在同一作用域的一组函数
隐藏 基类和派生类中,函数名相同
覆盖 基类和派生类中,函数的返回值,函数名,参数列表都相同,基类的函数是virtual虚函数(关于虚函数我们在后面进行说明)

如下代码示例:

#include <iostream>
using namespace std;

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
        virtual void show() { cout << "Base::show()" << endl; } //     a
	void show(int) { cout << "Base::show(int)" << endl; } //      b
protected:
	int ma;
};
class Derive : public Base
{
public:
	Derive(int data) :Base(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show()" << endl; } //       c
private:
	int mb;
};
int main()
{
	Derive d(10);
	d.show(); // 调用的是派生类自己的show方法
	d.Base::show(); // 指定了作用域,调用的是派生类从基类继承来的show方法
	//d.show(10); // 编译错误,不能调用,派生类的show方法把基类继承来的show方法给隐藏了
	return 0;
}

说明:

上述代码中,a 和 b是重载关系,因为他们函数名相同,函数的作用域也相同,b 和 c 不是重载关系,因为他们所处的作用域不同;a 和 c、b 和 c是隐藏关系,当派生类调用该成员函数时,就对基类的同名成员函数产生隐藏,调用的时派生类自身的成员函数,如果派生类想要调用基类的成员函数,就必须加上基类的作用域;a 和 c 时覆盖关系,覆盖关系主要指的的时虚函数表中的函数地址的覆盖重写。

赋值兼容性规则

继承结构,是从上到下的结构(即从基类到子类的结构),但只支持从下到上的转换,即(派生类带子类)。如图所示:

我们知道派生类是由基类派生出来的,派生类的内存大于或等于基类,(等于情况为派生类并不增加新的成员方法和新的成员变量)针对派生类对象和基类对象以下四种赋值方式,进行说明:

派生类对象 = 基类对象  错误。由图可知,由于派生类对象所占用的内存可能大于基类对象占用的内存,所以用基类对象给派生类对象赋值时,派生类自增加的成员变量的值是不确定的,这对于程序而言是不安全的,因此错误。

基类对象 = 派生类对象 ,正确,编译器只把派生类从基类继承过来的成员变量赋值给基类的对象;

基类指针/引用 = 派生类对象,正确,只能访问派生类从基类继承过来的成员。

派生类指针/引用 = 基类对象,错误,派生类占用的内存空间可能大于基类,因此派生类指针可能会访问到非法的内存空间。

总结:

所谓赋值兼容规则,即在任何需要基类对象的地方都可以用该基类的公有派生类的对象来代替,它主要包括以下情况:

1、派生类的对象可以赋值给基类的对象,此时的赋值操作主要是把派生类对象中所包含的对应基类的“对象”赋值给基类对象。而基类对象不能赋值给派生类对象,因为派生类对象中自增加的新成员变量无法通过基类对象赋值,基类对象只能对派生类从基类中派生的成员变量进行赋值,这会导致为知的错误;

2、派生类对象的地址可以赋值给其基类的指针变量,只能通过该指针访问派生类中由基类继承来的“对象”,不能访问派生类中的新成员,实际上就是将指针指向派生类中由基类继承来的“对象”,不能将一个基类对象的地址赋值给一个派生类的指针变量,因为派生类的指针变量可能指向一个未知的空间;

3、派生类对象可以初始化基类的引用,只能通过该引用访问派生类中由基类继承来的“对象”,不能访问派生类中的新成员变量,实际上就是为派生类中由基类继承来的“对象”起一个别名,不能用基类对象来初始化派生类的引用;
 

 

发布了40 篇原创文章 · 获赞 124 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/FDk_LCL/article/details/89456490