C++之继承

简介: C++之继承



一、继承的概念

1、概念

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,

承是类设计层次的复用。

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 _stuid; // 学号
};
class Teacher : public Person
{
protected:
    int _jobid; // 工号
};

继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和Teacher复用了Person的成员。

2、继承的定义

下面我们看到Person父类,也称作基类Student子类,也称作派生类

3、继承关系和访问限定符

我们知道访问限定符有 public、protected 和 private三种,而继承方式也是有三种。他们之间重要的关系如下:

注:不可见表示子类无法访问父类的私有成员。因此如果父类里有不想让子类访问的成员,就可以设为私有权限。

基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承。


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

派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片

或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象。

class Person
{
protected :
    string _name; // 姓名
    string _sex;  // 性别
    int _age; // 年龄
};
class Student : public Person
{
public :
    int _No ; // 学号
};
int main()
{
    Student sobj ;
 // 子类对象可以赋值给父类对象/指针/引用
    Person pobj = sobj ;
    Person* pp = &sobj;
    Person& rp = sobj;
}

三、继承下的子类的成员函数

父类

class Person
{
public:
  Person(const char* name = "ZDL")
    : _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, int num);
    Student(const Student& s);
    Student& operator=(const Student& s);
  ~Student();
protected:
  int _num;
};

1、构造函数

子类自己的成员,跟类和对象一样。从父类继承过来的成员需要去调用父类的构造函数。

Student(const char* name,int num)
    :Person(name)  //_name是由父类继承而来,需要去调用父类的构造函数
  ,_num(num)   //子类自己的成员自己初始化
{}

2、拷贝构造函数

子类自己的成员,跟类和对象一样(内置类型完成值拷贝,自定义类型调用它自己的拷贝构造)。从父类继承过来的成员需要去调用父类的拷贝构造函数。

Student(const Student& s)
    :Person::Person(s)//调用父类的拷贝构造
  ,_num(s._num)
{}

3、赋值运算符

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

4、析构函数

在继承的情况中,不需要显示调用父类的析构函数,编译器自动调用,先析构子类,再析构父类。只需要处理子类中的成员。

//子类的析构函数跟父类的析构函数构成隐藏
~Student()
{
  //处理子类自己的
}

四、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。


五、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子

类,都只有一个static成员实例 。


六、菱形继承问题

1、单继承

一个子类只有一个直接父类时称这个继承关系为单继承。

2、多继承

一个子类有两个或以上直接父类时称这个继承关系为多继承。

3、菱形继承

菱形继承是多继承的一种特殊情况。 如下图:

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。

在Assistant的对象中Person成员会有两份。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和

Teacher的继承Person时使用虚拟继承,加上关键字 virtual 即可解决问题。

为了方便观察,我们使用下面的例子来看看菱形继承的问题:

class A
{
public:
  int _a;
};
// class B : virtual public A
class B : public A
{
public:
  int _b;
};
// class C : virtual public A
class C : 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;
}

从上面的图中我们就可以看出在D中A的成员_a有两份,有数据冗余的问题。接下来我们了看看虚拟继承下的D中的情况。

这下我们发现 A 的 _a 成员只有一份了(红色框起来的),而 B 类和 C类中也分别多出了一个指针。那么这指针是个什么的呢?下面我们来讲一讲。  

从内存的结构看去,我们发现指针指向的内容为它到取到 _a 的位置的偏移量,这样我们就可以找到共用的_a。这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A(虚基类) 。


七、继承与组合

1、public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。

2、组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

3、优先使用对象组合,而不是类继承。

4、实际尽量多去用组合。组合的耦合度低,代码维护性好。要实现多态,也必须要继承。

目录
相关文章
|
1月前
|
安全 Java 编译器
C++进阶(1)——继承
本文系统讲解C++继承机制,涵盖继承定义、访问限定符、派生类默认成员函数、菱形虚拟继承原理及组合与继承对比,深入剖析其在代码复用与面向对象设计中的应用。
|
5月前
|
存储 安全 Java
c++--继承
c++作为面向对象的语言三大特点其中之一就是继承,那么继承到底有何奥妙呢?继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用,继承就是类方法的复用。
141 0
|
8月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
476 6
|
10月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
163 16
|
10月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
304 5
|
编译器 C++ 开发者
【C++】继承
C++中的继承是面向对象编程的核心特性之一,允许派生类继承基类的属性和方法,实现代码复用和类的层次结构。继承有三种类型:公有、私有和受保护继承,每种类型决定了派生类如何访问基类成员。此外,继承还涉及构造函数、析构函数、拷贝构造函数和赋值运算符的调用规则,以及解决多继承带来的二义性和数据冗余问题的虚拟继承。在设计类时,应谨慎选择继承和组合,以降低耦合度并提高代码的可维护性。
235 1
【C++】继承
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
201 11
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
158 1
|
C++
C++番外篇——虚拟继承解决数据冗余和二义性的原理
C++番外篇——虚拟继承解决数据冗余和二义性的原理
152 1
C++(二十)继承
本文介绍了C++中的继承特性,包括公有、保护和私有继承,并解释了虚继承的作用。通过示例展示了派生类如何从基类继承属性和方法,并保持自身的独特性。此外,还详细说明了派生类构造函数的语法格式及构造顺序,提供了具体的代码示例帮助理解。