美文网首页
C++学习(5)继承和派生

C++学习(5)继承和派生

作者: su945 | 来源:发表于2020-08-12 23:07 被阅读0次

    1.继承和派生

    • 继承:在定义一个新的类B时,如果该类与某
      个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。
    • 派生类:
      通过对基类进行修改和扩充得到的,在派生类中可以扩充新的成员变量和成员函数。
      派生类一经定义后,可以独立使用,不依赖于基类。
      派生类拥有基类的全部成员函数和成员变
      量,不论是private、 protected、 public 。
      在派生类的各个成员函数中,不能访问基类中的private成员。

    派生类的写法:
    class 派生类名: public 基类名
    {
    };

    class CStudent {
    private:
        string sName;
        int nAge;
    public:
        bool IsThreeGood() { };
        void SetName(const string & name)
        {
            sName = name;
        }
        //......
    };
    class CUndergraduateStudent : public CStudent {
    private:
        int nDepartment;
    public:
        bool IsThreeGood() { ...... }; //覆盖
        bool CanBaoYan() { .... };
    }; // 派生类的写法是:类名: public 基类名
    

    2.派生类对象的内存空间

    派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。 在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。


    内存空间.png

    3.继承关系和复合关系

    • 类之间的两种关系
      • 继承:“是”关系。
      – 基类 A, B是基类A的派生类。
      – 逻辑上要求:“一个B对象也是一个A对象”。
      • 复合:“有”关系。
      – 类C中“有” 成员变量k, k是类D的对象,则C和D是复合
      关系
      – 一般逻辑上要求:“D对象是C对象的固有属性或组成部
      分”。

    4.派生类覆盖基类成员

    • 覆盖
      派生类可以定义一个和基类成员同名的成员,这叫覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。

    5.类的保护成员

    • 基类的private成员:可以被下列函数访问
    – 基类的成员函数
    – 基类的友元函数


    • 基类的public成员:可以被下列函数访问
    – 基类的成员函数
    – 基类的友元函数
    – 派生类的成员函数
    – 派生类的友元函数
    – 其他的函数


    • 基类的protected成员:可以被下列函数访问
    – 基类的成员函数
    – 基类的友元函数
    – 派生类的成员函数可以访问当前对象的基类的保护成员


    6.派生类的构造函数

    • 在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数。


    • 调用基类构造函数的两种方式
    – 显式方式:在派生类的构造函数中,为基类的构造函数提供参数.
    derived::derived(arg_derivedlist):base(arg_base-list)
    – 隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数.


    • 派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数。
    包含成员对象的派生类的构造函数写法

    class Bug {
    private:
        int nLegs; int nColor;
    public:
        int nType;
        Bug(int legs, int color);
        void PrintBug() { };
    };
    class Skill {
    public:
        Skill(int n) { }
    };
    class FlyBug : public Bug {
        int nWings;
        Skill sk1, sk2;
    public:
        FlyBug(int legs, int color, int wings);
    };
    FlyBug::FlyBug(int legs, int color, int wings) :
        Bug(legs, color), sk1(5), sk2(color), nWings(wings) {
    }
    

    封闭派生类对象的构造函数的执行顺序
    在创建派生类的对象时:

    1. 先执行基类的构造函数,用以初始化派生类对象中从基类
      继承的成员;
    2. 再执行成员对象类的构造函数,用以初始化派生类对象中
      成员对象。
    3. 最后执行派生类自己的构造函数

    封闭派生类对象消亡时析构函数的执行顺序
    在派生类对象消亡时:

    1. 先执行派生类自己的析构函数
    2. 再依次执行各成员对象类的析构函数
    3. 最后执行基类的析构函数
      析构函数的调用顺序与构造函数的调用顺序相反。

    7.public继承的赋值兼容规则

    1) 派生类的对象可以赋值给基类对象
    b = d;
    2) 派生类对象可以初始化基类引用
    base & br = d;
    3) 派生类对象的地址可以赋值给基类指针
    base * pb = & d;


    如果派生方式是 private或protected,则上述三条不可行。

    8.protected继承和private继承

    • protected继承时,基类的public成员和protected成员成为派生类的protected成员。
    • private继承时,基类的public成员成为派生类的private成员,基类的protected成员成
    为派生类的不可访问成员。
    • protected和private继承不是“是”的关系。

    9.基类与派生类的指针强制转换

     公有派生的情况下,派生类对象的指针可以直接赋值给基类指针
    Base * ptrBase = &objDerived;
    ptrBase指向的是一个Derived类的对象;
    *ptrBase可以看作一个Base类的对象,访问它的public成员直接通过ptrBase即可,但不能通过ptrBase访问objDerived对象中属于Derived类而不属于Base类的成员


     即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。


     通过强制指针类型转换,可以把ptrBase转换成Derived类的指针
    Base * ptrBase = &objDerived;
    Derived *ptrDerived = (Derived * ) ptrBase;
    程序员要保证ptrBase指向的是一个Derived类的对象,否则很容易会出错。

    #include <iostream>
    using namespace std;
    class Base {
    protected:
        int n;
    public:
        Base(int i) :n(i) {
            cout << "Base " << n <<
                " constructed" << endl;
        }
        ~Base() {
            cout << "Base " << n <<
                " destructed" << endl;
        }
        void Print() { cout << "Base:n=" << n << endl; }
    };
    
    class Derived :public Base {
    public:
        int v;
        Derived(int i) :Base(i), v(2 * i) {
            cout << "Derived constructed" << endl;
        }
        ~Derived() {
            cout << "Derived destructed" << endl;
        }
        void Func() { };
        void Print() {
            cout << "Derived:v=" << v << endl;
            cout << "Derived:n=" << n << endl;
        }
    };
    
    int main() {
        Base objBase(5);
        Derived objDerived(3);
        Base * pBase = &objDerived;
        //pBase->Func(); //err;Base类没有Func()成员函数
        //pBase->v = 5; //err; Base类没有v成员变量
        pBase->Print();
        //Derived * pDerived = & objBase; //error
        Derived * pDerived = (Derived *)(&objBase);
        pDerived->Print(); //慎用,可能出现不可预期的错误
        pDerived->v = 128; //往别人的空间里写入数据,会有问题
        objDerived.Print();
        return 0;
    }
    /*
    输出结果:
    Base 5 constructed
    Base 3 constructed
    Derived constructed
    Base:n=3
    Derived:v=1245104 //pDerived->n 位于别人的空间里
    Derived:n=5
    Derived:v=6
    Derived:n=3
    Derived destructed
    Base 3 destructed
    Base 5 destructed
    */
    

    10.直接基类和间接基类

    在声明派生类时, 只需要列出它的直接基类
    – 派生类沿着类的层次自动向上继承它的间接基类
    – 派生类的成员包括
    • 派生类自己定义的成员
    • 直接基类中的所有成员
    • 所有间接基类的全部成员

    相关文章

      网友评论

          本文标题:C++学习(5)继承和派生

          本文链接:https://www.haomeiwen.com/subject/zvnmdktx.html