目录

继承关系

赋值兼容规则

继承中的作用域

派生类的默认成员函数

构造函数

拷贝构造函数

派生类的 operator=

派生类的析构函数

思考题:设计出一个类A,让这个类不能被继承

继承与友元

复杂的菱形继承及菱形虚拟继承

*C++编译器如何通过虚继承解决数据冗余和二义性问题?(通过内存调试观察)

继承与组合


继承关系

以学生类和老师类为例

#include<iostream>
#include<string>
using namespace std;class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter";//姓名int _age = 18; //年龄
};
class Student : public Person
{
protected:int _studi;//学号
};
class Teacher : public Person
{
protected:int _jobid;//工号
};int main()
{Student s;Teacher t;s.Print();t.Print();return 0;
}

基类的其他成员在子类的访问方式取小  public>proteced>private,基类的私有成员在派生类中不可见。

class Student : private Person
{
protected:int _studi;//学号
};

 私有成员和私有继承

出现错误

在类外收到访问限定符的限制,但是在类里面就不收到该限制。若在子类中设置一个函数,

该函数无法被调用

换成公有继承,也错误

综上,父类的私有成员在子类不可见。不可见的意思是:内存上子类对象有这个成员,但是语法规定了我们不能去访问

赋值兼容规则

父子类对象之间能否互相赋值?

class Person
{
protected:string _name; //姓名string _sex;  //性别int    _age;  //年龄
};
class Student : public Person
{
public:int _No;//
};
int main()
{Person p;Student s;p = s;s = p;return 0;
}

使用强制类型转换

综上子类可以给父类,反之不行。派生类对象赋值给基类的对象/基类的指针/基类的引用(将子类给父类的过程)称为切割/切片。切割的三种情况如下

继承中的作用域

class Person
{
protected:string _name = "x";//姓名int _num = 111;    //身份证号
};class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << "身份证号:" << Person::_num << endl;cout << "学号:" << _num << endl;}
protected:int _num = 999;//学号
};
int main
{Student sreturn 0;
}

出现两个num,一般访问时访问的是子类的num,若想访问父类的num,加上限定符即可

隐藏(重定义): 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问

下面代码为例不能编译通过

class A
{
public:void fun(){cout << "fun()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
void Test()
{B b;b.fun(10);
}

B中的fun和A中的fun不是构成重载,因为不是在同一作用域,B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。想调用函数应该指定作用域

派生类的默认成员函数

构造函数


class Person
{
public:Person(const char* name = "peter"):_name(name){cout << "Person()" << endl;}Person(const Person&):_name(p._name){cout << "Person(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name;//姓名
};class Student : public Person
{
public:Student(const char* name, int id):_name(name),id(id){}private:int _id;
};
int main()
{Student s("张三",1);return 0;
}

程序编译出错

不初始化name反而可以编译通过

调试过程中打印了一个person,说明调用了person函数,但派生类中我们没有调用person。则在初始化列表阶段会自动调用

改正如下。子类对象中先调用父类再调用子类。调用父类构造函数初始化继承父类部分,再初始化自己的成员

拷贝构造函数

Student(const Student& s):Person(s)  //-> s传递给Person& s是一个切片行为,_id(s._id){//类似构造函数cout << "Student(const Student& s)" << endl;}

派生类的 operator=

Person& operator(const Person& p){cout << "Person opertor=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}
Student& operator=(const Student& s){if (this != &s){operator=(s);_id = s._id;}return *this;}

运行后报了一个栈溢出的错误:子类的operator赋值和父类的operator赋值构成隐藏关系(同名成员)

改正:加上作用域即可

Student& operator=(const Student& s){if (this != &s){Person::operator=(s);_id = s._id;}return *this;}

派生类的析构函数

//父类的析构函数
~Person(){cout << "~Person()" << endl;}
~Student(){~Person();//清理自己的}

无法显式地调用父类的: 编译器认为子类的析构函数和父类的析构函数构成隐藏。因为后面多态的一些原因,任何类析构函数名都会被统一处理成destructor()

改正如下:指定父类即可

~Student(){Person::~Person();}

 调用的时候父类被析构了两次?

按理说构造函数先调用父类再调用子类,析构函数先调用子类再调用父类,中间有一次析构是多余的

为了保证析构时,保持先子后父的后进先出的顺序析构,子类析构函数完成后,会自动去调用父类的析构函数。调用了两次没有崩溃的原因:析构函数是完成清理工作,子类中没有需要清理的东西

思考题:设计出一个类A,让这个类不能被继承

父类A的构造函数私有化后,B就无法构造对象


class A
{
private:A(){}
};class B :public A
{};int main()
{B b;return 0;
}

继承与友元

成员可以继承,但友元关系不能被继承

class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string_name;//姓名
};class Student : public Person
{
protected:int _stuNum; //学号
};void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}void main()
{Person p;Student s;Display(p, s);
}

如果想访问,再增加一个友元即可

复杂的菱形继承及菱形虚拟继承

 class Person
{
public:string_name;//姓名
};class Student :   public Person
{
protected:int _num; //学号
};class Teacher :   public Person
{
protected:int _id; //职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; //主修课程
};void Test()
{//这样会有二义性无法明确知道访问的是哪一个Assistant a;a._name = "小张";
}

报错如下:

指定作用域即可解决

void Test()
{Assistant a;a.Student::_name = "小张";a.Teacher::_name = "张老师"
}

加入virtual后name变为同一个

class Person
{
public:string_name;//姓名
};class Student : virtual public Person
{
protected:int _num; //学号
};class Teacher : virtual public Person
{
protected:int _id; //职工编号
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; //主修课程
};void Test()
{Assistant a;a.Student::_name = "小张";a.Teacher::_name = "张老师"
}

综上,虚继承可以解决数据冗余和二义性

*C++编译器如何通过虚继承解决数据冗余和二义性问题?(通过内存调试观察)

1.通过监视窗口已经看不到真实的存在,因为监视窗口被编译器处理过

2.建议使用内存窗口来查看

下面展示非虚继承

#include<iostream>
using namespace std;class A
{
public://int _a[10000];int _a;
};class B : public A
//class B : virtual public A
{
public:int _b;
};class C : public A
//class C : virtual public A
{
public:int _c;
};class D : public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;return 0;
}

打断点后进入内存窗口调试

不是虚继承的时候,B继承A,那么内存中是先A后B。虚继承以后,类似切片b = d,这个时候在内存中就不是先A后B了

继承与组合

C++ 继承(补充)相关推荐

  1. 黑马毕向东Java课程笔记(day07):面向对象(第三部分)继承+抽象类+模板方法设计模式+接口+final+继承补充(就业班)

      在这一部分中,我们将讲解有关继承的相关内容,包括继承的概述.继承的特点.super关键字.函数覆盖.子类的实例化过程.final关键字这几个部分的内容. 1.继承的概述以及特点 1.1.概述    ...

  2. java基础之继承补充和抽象类

    继承的补充和抽象类 1.子类和父类中出现了同名的变量该如何访问?(super) 使用super关键字可以直接指定父类空间 格式: super.成员变量 访问成员变量的时候特点: 如果所指定的空间找不到 ...

  3. GContracts1.1.0支持前置/后置条件的继承

    2019独角兽企业重金招聘Python工程师标准>>> GContracts 1.1.0已于端午节前(6月14日)发布了.Andre Steingress 在其博文中,讲述了该版本中 ...

  4. day21 面向对象之继承和组合

    继承 组合 OOP的三大特征之一:封装,继承,多态 继承 什么是继承? # 继承是一种关系,描述两个对象之间,什么是什么的关系 例如:麦兜,佩奇,猪刚鬣 都是猪啊, # 在程序中,继承描述的是类和类之 ...

  5. 面向对象(类/对象/封装/继承/static/内存类加载)

    >面向对象:万物皆对象: >类和对象: 从代码的角度来说:类是代码的载体. 从逻辑角度来说:类是静态文件:对象是内存中的地址: 形象的来说,类就是一个模具,是用来生产对象的:对象是基于类产 ...

  6. 经过前面几天的学习已经把对象的三大特性之一封装学完了现在开始学习接下来的继承和多态

    继承: 1.继承的概述 **继承在java中指的是"一个类"可以"继承自""另一个类". "被继承的类"叫做: 父类/超 ...

  7. 1-3_基本概念_程序进程

    程序 程序通常以两种面目示人.其一为源码形式,由使用编程语言(比如,C语言)写成的一系列语句组成,是人类可以阅读的文本文件.想要执行程序,则需将源码转换为第二种形式--计算机可以理解的二进制机器语言指 ...

  8. java代码分类_08 java代码块的概述和分类

    08.01_面向对象(代码块的概述和分类) A:代码块概述 在Java中,使用{}括起来的代码被称为代码块. B:代码块分类 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代 ...

  9. 面向对象特征:封装、多态 以及 @propetry装饰器

    (继承补充)组合  obj=fun()#对象  obj.attr=foo()#对象的属性等于另一个对象  什么是组合:     A类的对象具备某一个属性,该属性的值是B类的对象    基于这种方式就把 ...

最新文章

  1. mysql query browswer_MySQL数据库新特性之存储过程入门教程
  2. python处理流程-python的处理流程
  3. 【python】一个简单的贪婪爬虫
  4. 返璞归真的Linux BFS调度器
  5. 如何修改数据库表或存储过程的所有者
  6. 【渝粤题库】国家开放大学2021春2757宠物饲养题目
  7. struts2值栈,OGNL表达式,interceptor
  8. lol最克制诺手的英雄_LOL:究竟有没有完美克制诺手的英雄?时光上单或可一战?...
  9. django学习随笔:ManagementUtility
  10. python简述列表特征_python高级特性简介
  11. 【运动学】基于matlab GUI地球自转模拟【含Matlab源码 1115期】
  12. 可以提高DeepFaceLab(DeepFake)合成最终视频速度的方法
  13. 6、Latex学习笔记之参考文献篇
  14. 加那些YY主播的微信为何要花钱?
  15. 输入一个字符串,对字符中的各个英文字符,数字,空格进行统计。 按照统计个数由多到少输出统计结果,如果统计的个数相同,则按照ASII码由小到大排序输出
  16. 用老虎机小例子理解Multi-armed Bandit Learning
  17. python机器学习——十次交叉验证训练的数据准备算法
  18. 神经网络与深度学习一 :介绍深度学习
  19. linux python 路径获取
  20. 对数损失函数与最大似然损失函数

热门文章

  1. excel快速输入金额大写
  2. 中国省级GDP增长目标(2000-2018年)
  3. 一个老果粉的华为Mate20 Pro深度体验
  4. 寒假集训重要内容总结
  5. 计算机分析报告摘要怎么写,计算机实践论文摘要怎么写 计算机实践论文摘要范文参考...
  6. 企业级的HttpsNginx实战部署
  7. iapp退出软件按钮代码_昆仑通态软件应用(23):酒店触摸屏点菜系统的设计(案例)...
  8. ECmall邀请注册功能构想和实现
  9. Python的一些读书笔记
  10. 玩转iOS开发:iOS 11 新特性《UIKit新特性的基本认识》