当前位置: 首页 > article >正文

【c++】继承

目录

一、继承的表现

子类对父类成员的访问权限

二、父类与子类之间的相互赋值

三、继承的作用域

如果是父类和子类构成隐藏呢?

 四、子类的成员函数怎么写

1.default构造函数

 2.析构函数

所以析构函数不需要我们显式调用。

五、继承与友元函数

六、继承与静态成员

七、菱形继承

 八、虚基表


 

        继承是c++对于类复用的重要特性,与之前写过的对于函数代码的复用相似。

一、继承的表现

class A
{
public:
	int _a;
};

class B:public A
{
public:
	int _b; 
};

class C :public A
{
public:
	int _c;
};

 在这个关系中,类B和类C都继承了类A,且均为public继承。

子类对父类成员的访问权限

无论是哪种继承,子类均能够完全继承父类的成员,但是访问权限不同。

继承方式影响的是类外通过子类访问父类的权限

public:子类可以访问,类外也可以访问

protected:子类可以访问,但是类外不能访问

private:子类和类外均不能访问

二、父类与子类之间的相互赋值

因为子类是从父类得到东西,并且加上了自己的东西,那么子类的成员是要比父类的多的,所以可得:子类可以赋值给父类,并且子类多的那一部分东西不会给父类,这个操作叫做切片

class A
{
public:
	int _a;
};

class B:public A
{
public:
	B(const int& b = 0)
		:_b(b)
	{}
	int _b; 
};

在这里我没有给类A实现他的默认构造函数。

创建了一个b对象,b中有两个成员,一个是类A,一个是_b,类A中有成员_a, 相当于b中有_a和_b。

 可以看到,b中的成员在给a进行赋值的时候并不会出错,只是把b的父类有的东西赋值给了a。

三、继承的作用域

在父类和子类中是允许出现两个相同名字的成员的,这种关系叫做隐藏,也叫重定义。 

class Parent
{
public:
	Parent()
		:_num(0)
		,_parent(0)
	{
		cout << "Parent()" << endl;
	}
	~Parent()
	{
		_num = _parent = 0;
		cout << "~Parent()" << endl;
	}

	int _num;
	int _parent;
};

class Child1:public Parent
{
public:
	int _child;
};

class Child2 :public Parent
{
public:
	int _child;
};

在这个关系中,类child1和类child2都有共同的成员变量_child,构成隐藏关系。

在进行访问时,互不干扰。

这是位于同一层的关系,他们都是父类的子类。(辈分相同)

如果是父类和子类构成隐藏呢?

 

 可知是由于就近原则的关系,直接对于c3的同名成员变量进行操作是在给c3的成员继续赋值。

如果要访问父类的同名成员需要加上作用域限定符:

 四、子类的成员函数怎么写

1.default构造函数

子类包含有父类的成员,在子类进行构造的时候,先调用父类的构造函数,然后才会调用自己的构造函数。

在子类进行析构的时候,先调用自己的析构函数后才会调用父类的析构函数。(后创建的先销毁)

显示调用的原因是我没有写Parent类的默认构造函数。

 

 2.析构函数

他会先调用自己的析构函数后去调用父类的析构函数。 

没写父类析构函数,也没有显示调用:


 没写父类析构函数,显示调用:


写析构,显示调用:

 因为在显示调用时候父类资源已经被释放,但是在子类的析构函数结束时,它会自动再调用一次父类的析构函数,所以出现内存问题。

所以析构函数不需要我们显式调用。

五、继承与友元函数

父类的友元函数并不是子类的友元函数,友元函数一向如此。

六、继承与静态成员

静态成员在子类中也存在,和父类的是同一个东西。

七、菱形继承

继承关系呈现菱形的继承是菱形继承,也即:一个父类有多个子类,一个孙子类继承了多于两个的子类,此时就构成了菱形继承。

菱形继承会造成很多不必要的麻烦:比如数据冗余和二义性。

数据冗余是指在父类和子类中都会存在相同的一个父类。

 二义性是指在孙子类中访问父类的成员时不知道访问的是谁的。

因为两个子类中都存在这个父类的成员。

需要加上作用域限定符: 

 

但其实我们想要的是一块空间存储类A就行了,所以加上关键字virtual使其成为虚继承:

 这样就解决了数据冗余的问题,也解决了二义性的问题:

 八、虚基表

 观察内存窗口我们可以发现父类A的成员存在了类D的后面,不属于任何类。

由继承关系可以知道,在d的内存空间中,是严格按照继承顺序来排列的,先是b的空间,再是c的空间,再是d自己的空间,最后是公共的空间用来存放父类。

由内存窗口可以知道,分别有两个字节的空间不是存放子类的数据的,这两个字节的数据就是虚基表,用来存放当前地址到父类(公共空间)的距离,不然会找不到这块地址的。

由于虚基表的存在,每个类也都不是本来类型之和的大小了。 

不加virtual关键字的情况:

 sizeof(D) = (A的大小)sizeof(int)*2 + (B的大小)sizeof(int)*2 + (D的一个成员)sizeof(int) = 20

所以多出来的12个字节用来存放虚基表。


http://www.kler.cn/a/321.html

相关文章:

  • Flutter 实现文本缩放学习
  • 六十:HTTP/2与gRPC框架
  • 逻辑控制语句
  • `we_chat_union_id IS NOT NULL` 和 `we_chat_union_id != ‘‘` 这两个条件之间的区别
  • AMD | GPU | 深度学习 | 如何使用
  • 【信息系统项目管理师】第13章:项目资源管理-基础和过程 考点梳理
  • 自学大数据第六天~HDFS命令(一)
  • Linux基础命令大全(下)
  • python+django+vue图书个性化推荐系统
  • Vue3之父子组件通过事件通信
  • 高速PCB设计指南系列(四)
  • Java for循环嵌套for循环,你需要懂的代码性能优化技巧
  • 常见的HTTP状态码
  • HTTP 3.0来了,UDP取代TCP成为基础协议,TCP究竟输在哪里?
  • 滑动窗口算法
  • CentOS定时任务——crontab
  • Vue 3.0 单文件组件 【Vue3 从零开始】
  • 猿人学爬虫第1题- js混滑–源码乱码
  • SpringBoot:SpringBoot 的底层运行原理解析
  • TCP/IP协议
  • 【电赛MSP430系列】GPIO、LED、按键、时钟、中断、串口、定时器、PWM、ADC
  • 马上要面试了,还有八股文没理解?让ChatGPT来给你讲讲吧——如何更好使用ChatGPT?
  • 【数据结构】链表OJ
  • R语言编程基础
  • 面试官问百万数据excel导出功能如何实现?
  • 任何时候都不要在 for 循环中删除 List 集合元素!!!