继承(C++) 上

简介: 继承(C++)

继承的概念及定义


继承的概念


继承是面向对象程序设计使代码可以复用的重要手段,允许程序在保持原有类特性的基础上进行扩展,增加功能,所产生的新类,称作派生类。继承呈现了面向对象程序设计的层次结构,体现了有简单到复杂的过程,继承是类设计层次的复用


class Person
{
public:
  void Print()
  {
  cout << "name:" << _name << endl;
  cout << "age:" << _age << endl;
  }
protected:
  string _name="zhangsan";//姓名
  int _age=18;//年龄
};
class Student :public Person
{
protected:
  int _stuid;//学号
};
class Teacher :public Person
{
protected:
  int _jobid;//工号
};
int main()
{
  Student s;
  Teacher t;
  s.Print();
  t.Print();
  return 0;
}


d4fedc3b99714dc99100e7ecbc7d5071_89550968189f4b12b18a35a56cae50f4.png


继承后父类的 Person的成员,都会变成子类的一部分,在 Print的打印中体验了复用的效果


继承的定义


定义格式


Person称作父类,也是基类;Teacher称作子类,也是派生类


bd3f68574119508b44c6d3b604b4d3b5_d16cb82cd4014f3ab4d6a245cfa6b6e1.png


继承关系和访问限定符



5fa2934f7cc291509495852c08eebf16_14c325c0f855433ca3cbeee19b74fd2b.png


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


image.png


最终的继承成员:类成员和继承方式中权限较低的那个


基类和派生类对象赋值转换


派生类对象可以赋值给基类的对象/指针/引用,与以往所学习的有所不同,子类可以赋值给父类


观察下列代码


int main()
{
  int i = 1;
  double d = 2.2;
  i = d;
  const int& ri = d;
  return 0;
}

当 i=d时,并不是直接赋值,而是先创建一个临时变量,临时变量中的值为2,临时变量具有常性,所以在下面引用时,必须加上 const否则程序便会崩溃


34cfaa4327ea09fd1c55f91cf6c50311_4de1fc6f1af3454c83d6d8369baf73dd.png


上面的问题在继承中就不会出现,因为赋值的原理都不同;继承中的赋值并不会产生临时变量,而是直接将子类赋值给父类,简单来说是子类中与父类相同类型的数据赋值给父类,也称切片


int main()
{
  Person pn;
  Teacher t;
  pn = t;
  return 0;
}


c4c0e241e87e6cae4f9bd0d2118a7db9_eb5576a47f2b405f8a6ff0f0a963aec3.png


继承中的作用域


在继承中基类和派生类都有独立的作用域

当子类和父类中有同名成员时,子类成员将屏蔽父类对同名成员的直接访问,此情况称作隐藏,也称作重定义

如果成员函数也同名,也构成隐藏

class Person
{
protected:
  string _name = "zhangsan";//姓名
  int _age = 18;//年龄
};
class Student :public Person
{
public:
  void Print()
  {
  cout << "姓名:" << _name << endl;
  cout << "年龄:" << _age << endl;
  }
protected:
  int _age=20;//年龄
};
void test()
{
  Student s;
  s.Print();
}


c97c187d7e7a3981914c58d5e2e9f8ca_7c02107315794ea8b203556e48164195.png


Student,Person中的_age学号构成隐藏,程序运行之后打印的是子类中的_age,将父类中的_age隐藏了起来


如果想访问父类中的_age,可以做如下操作


class Student :public Person
{
public:
  void Print()
  {
  cout << "姓名:" << _name << endl;
  cout << "年龄:" << Person::_age << endl;
  }
protected:
  int _age=20;//年龄
};


8caef3bf7f89ec962dbcfeea43837d60_56dc2b04d76449cbbb2829ac11f35473.png


派生类的默认成员函数


普通类的成员


  1. 内置类型
  2. 自定义类型


65fe0839e753de5a3b9bb251bd4586d7_797402e14c014ea6bc41a399ce4375a5.png


派生类的成员


  1. 基类对象
  2. 派生类的内置类型
  3. 派生类的自定义类型


派生类中基类对象调用基类对应的函数完成初始化/清理/拷贝,内置类型/自定义类型的处理与普通类一致


class Person
{
public:
  Person(const char* name = "zhangsan")
  :_name(name)
  {
  cout << "Person()" << endl;
  }
  Person(const Person& p)
  :_name(p._name)
  {
  cout << "Person(const Person& p)" << endl;
  }
  Person& operator=(const Person& p)
  {
  cout << "Person& operator=(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)
  :Person(name)
  ,_stuid(210202)
  {
  cout << "Student()" << endl;
  }
  Student(const Student& st)
  :Person(st)
  ,_stuid(st._stuid)
  {
  cout << "Student(const Student& st)" << endl;
  }
  Student& operator=(const Student& st)
  {
  cout << "Student& operator=(const Student& st)" << endl;
  if (this != &st)
  {
    Person::operator = (st);
    _stuid = st._stuid;
  }
  return *this;
  }
  ~Student()
  {
  Person::~Person();
  cout << "~Student()" << endl;
  }
protected:
  int _stuid;
};
int main()
{
  Student st("zhangsan");
  return 0;
}


c73e0cc420f48587591e096fbe52cb8f_8f5cf0af9b24437e988e0aa145968c58.png


析构函数有所区别,子类析构函数和父类析构函数构成隐藏关系,由于多态的要求,所有的析构函数都会特殊处理成destructor函数名,所以在子类的析构函数中需要加上访问限定符Person::才能调用父类的析构函数;从上面的打印结果来看,父类的析构多调用了一次,只是为什么呢???


2b4ea51a97fcadaf370f24bd0a30c518_382fd254028d4ef68812d89130e8d816.png


通过查看汇编,我们发现,在子类的析构函数执行结束后,编译器会自动调用父类的析构函数进行资源清理工作;也就是说,我们不需要在子类中调用父类的析构函数


删除子类中调用的父类析构函数运行结果如下


b9d8ddcb4b428f8da4a64e5ca9ca7c50_6af11c66c7184bf9aba0bda2e981ba67.png


仔细观察发现其中也有些不寻常,子类后创建为什么先析构呢?

其实这也是析构函数特殊的地方:子类先析构,父类再析构,子类析构函数中不需要显示调用父类析构,子类析构结束后编译器会自动调用父类析构


目录
相关文章
|
1月前
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
33 1
【C++】继承
|
2月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
92 11
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
67 1
|
2月前
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
49 1
|
2月前
|
安全 编译器 程序员
C++的忠实粉丝-继承的热情(1)
C++的忠实粉丝-继承的热情(1)
22 0
|
2月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
41 0
|
2月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
44 0
|
3月前
|
C++
C++(二十)继承
本文介绍了C++中的继承特性,包括公有、保护和私有继承,并解释了虚继承的作用。通过示例展示了派生类如何从基类继承属性和方法,并保持自身的独特性。此外,还详细说明了派生类构造函数的语法格式及构造顺序,提供了具体的代码示例帮助理解。
|
3月前
|
C++
c++继承层次结构实践
这篇文章通过多个示例代码,讲解了C++中继承层次结构的实践应用,包括多态、抽象类引用、基类调用派生类函数,以及基类指针引用派生类对象的情况,并提供了相关的参考链接。
|
4月前
|
安全 Java 编译器