欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

继承与派生

程序员文章站 2024-03-15 18:06:48
...

继承


前言

  • 为什么会有继承?
  • 派生类
  • 基类成员在派生类中的访问属性
  • 隐藏
  • 派生类默认成员函数
  • 菱形继承

<一>为什么会有继承

提高代码的可重用性。
(无须修改已有类,只需在已有类的基础上,通过增加少量代码或修改少量代码的方法得到新类,从而较好的解决了代码重用的问题,形成类的层次结构)

<二>派生类

从已有类派生出新类时,可以在派生类内完成以下几种功能:

1.可以增加新的成员(成员函数、成员变量);
2.可以对 基类的成员进行重定义(同名成员隐藏)
3.可以改变基类成员在派生类中的访问属性。

<三>基类成员在派生类中的访问属性

继承与派生
由上表可直观看出:
(1)基类中的私有成员,无论以何种方式继承,基类中的私有成员在派生类中都是不可直接访问的。故,在设计基类时,总要为它的私有数据成员提供公有的成员函数,以便使派生类可以间接访问这些数据成员
(2)基类中的公有成员和保护成员,在派生类中的访问属性与继承方式密切相关。(详情参考上表)

<四>隐藏(重定义)

在继承体系中,子类和父类都有独立的作用域
子类和父类具有同名的成员(同名成员变量/成员函数),子类成员将屏蔽父类对成员的直接访问。当子类对象或者成员函数要访问父类的同名成员时,必须加上作用域

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
    Person(const char *name="")
        :_name(name)
    {
        cout << "Person()" << endl;
    }
    void Display()
    {
        cout << _name << endl;
    }
private:
    string _name;
};

class Student :public Person
{
public:
    Student(const char* name="",const char *number="")
        :_name(name),
        _number(number)
    {
        cout << "Student()" << endl;
    }
    void Display()
    {
        cout <<_name<<' '<<_number<< endl;
    }
private:
    string _name;
    string _number;
};
int main()
{
    Person p("Peter");
    Student s("Mary","123456");           //此时,s对象隐藏了父类对象的_name成员变量

    s.Display();         //子类Display()与父类Display()成员函数函数名同名,构成隐藏(即:重定义)
    s.Person::Display(); //子类对象想访问父类的Display(),必须加上类域。

    system("pause");
    return 0;
}

继承与派生

<五>派生类默认成员函数

1.派生类的构造函数和析构函数的调用顺序

通常情况下,

创建派生类对象时,首调用基类的构造函数,随后调用派生类的构造函数;
撤销派生类对象时,首调用派生类的析构函数,随后调用基类的析构函数。

2.派生类构造函数和析构函数的构造规则

(1)构造函数

*当基类的构造函数没有参数/没有显示定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数。此时系统自动调用基类的无参构造函数

*当基类构造函数只有含参数的时,派生类必须定义构造函数(以提供把参数传递给基类的构造函数途径),且需显示在初始化成员列表中调用基类的构造函数

即为以下格式:

派生类名(参数总表)
       :基类名(参数表)
{  
   派生类新增数据成员的初始化语句;
}

继承与派生
(2)析构函数
派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作。基类的清理工作仍然由基类的析构函数负责(在执行派生类的析构函数时,系统会自动调用基类的构造函数,对基类的对象进行清理)

派生类的析构函数在其内部对基类的构造函数构成了隐藏
(看似派生类的析构函数与基类的析构函数函数名不同,但实际在底层(即汇编层),编译器将基类和派生类的析构函数名均处理成了相同的函数名_destructor)

注:
派生类的默认的成员函数都是合成的。

<六>菱形继承

1.继承分类(按照派生类所继承的基类是否是单一的)

(1)单继承
(2)多继承

2.菱形继承产生的原因:
由于多继承的存在,就有可能出现菱形继承。
那么,何为菱形继承呢?(解释见下图)
继承与派生

示例如下:

#include<iostream>
#include<string>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A()" << endl;
    }
private:
    int _a;
};

class B:public A                       //B类是由A类派生下来的
{
{
public:
    B()
    {
        cout << "B()" << endl;
    }

private:
    int _b;
};

class C:public A                       //C类也是由A类派生下来的
{
public:
    C()
    {
        cout << "C()" << endl;
    }
private:
    int _c;
};

class D : public B, public C          //D类是由A类和B类派生下来的
{
public:
    D()
    {
        cout << "D()" << endl;
    }
private:
    int _d;
};

int main()
{
    D d;                              //此时d对象中含有两个_a(其中一个是由A类派生的,另一个是由B类派生的)

    system("pause");
    return 0;
}

继承与派生
3.如何解决菱形继承导致的问题呢?
(1)二义性 ——————————> 使用时,加类域(假设是B类的_a成员,则 表示为B::_a)
(2)数据冗余——————————> 虚继承 ——————> 生成虚基表

  • 未使用虚继承代码如下:
class A
{
public:
    A()
    {
        cout<<"A()"<<endl;
    }
public:
    int _a;
};

class B:virtual public A
{
public:
    B()
    {
        cout<<"B()"<<endl;
    }

public:
    int _b;
};

class C:virtual public A
{
public:
    C()
    {
        cout<<"C()"<<endl;
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    D()
    {
        cout<<"D()"<<endl;
    }
public:
    int _d;
};

int main()
{
    D d;
    d.B::_a = 1;            
    d.C::_a = 2;   //d中有两份_a,分别是B类和C类继承A类的成员,为解决二义性,使用时添加类域   
    d._b = 3;
    d._c = 4;
    d._d = 5;

    cout << sizeof(D) << endl;      //20(=(8)+(8)+4)

    system("pause");
    return 0;
}

继承与派生
- 使用虚继承代码如下:

class A
{
public:
    A()
    {
        cout<<"A()"<<endl;
    }
public:
    int _a;
};

class B:virtual public A
{
public:
    B()
    {
        cout<<"B()"<<endl;
    }

public:
    int _b;
};

class C:virtual public A
{
public:
    C()
    {
        cout<<"C()"<<endl;
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    D()
    {
        cout<<"D()"<<endl;
    }
public:
    int _d;
};

int main()
{
    D d;
    //d.B::_a = 1;            
    //d.C::_a = 2;   //虚继承后,_a只有一个,解决了二义性(即B::_a==C::_a),即:可写如下表达式
    d._a=2;
    d._b = 3;
    d._c = 4;
    d._d = 5;

    cout << sizeof(D) << endl;      //24(=(8)+(8)+4+(4))

    system("pause");
    return 0;
}

继承与派生

相关标签: 继承