zoukankan      html  css  js  c++  java
  • 继承(一)

    1. 继承的概念和意义

    类之间的关系

    在C++中,类之间可以有直接的关联关系,包括组合关系继承关系

    • 组合关系:整体与部分的关系
    • 继承关系:父子关系

    组合关系

    组合关系描述的是类之间整体与部分的关系,具有以下特点

    • 将其他类的对象作为当前类的成员变量使用
    • 当前类的对象与成员对象的生命期相同
    • 成员对象在用法上与普通对象完全一致,具有等同地位

    /*描述class的组合关系*/
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Memory
    {
    public:
        Memory()
        {
            cout << "Memory()" << endl;
        }
        ~Memory()
        {
            cout << "~Memory()" << endl;
        }
    };
    
    class Disk
    {
    public:
        Disk()
        {
            cout << "Disk()" << endl;
        }
        ~Disk()
        {
            cout << "~Disk()" << endl;
        }
    };
    
    class CPU
    {
    public:
        CPU()
        {
            cout << "CPU()" << endl;
        }
        ~CPU()
        {
            cout << "~CPU()" << endl;
        }
    };
    
    class MainBoard
    {
    public:
        MainBoard()
        {
            cout << "MainBoard()" << endl;
        }
        ~MainBoard()
        {
            cout << "~MainBoard()" << endl;
        }
    };
    
    class Computer
    {
    private:
        /*必须是其他类的对象,不能是指针,否则无法构成组合关系*/
        Memory mMem;
        Disk mDisk;
        CPU mCPU;
        MainBoard mMainBoard;
    public:
        Computer()
        {
            cout << "Computer()" << endl;
        }
        void power()
        {
            cout << "power()" << endl;
        }
        void reset()
        {
            cout << "reset()" << endl;
        }
        ~Computer()
        {
            cout << "~Computer()" << endl;
        }
    };
    
    int main()
    {
        Computer c;
    
        return 0;
    }
    

    继承关系

    继承关系描述的是类之间的父子关系,父类为基类,子类为派生类

    • 子类拥有父类的所有属性和方法,还可以添加父类没有的属性和方法
    • 子类是一种特殊的父类,子类对象可以当作父类对象使用,可以初始化父类对象,也可以给父类对象赋值
    • 继承是C++中代码复用的重要手段,通过继承,可以获得父类的所有功能,还可以在子类中重写已有功能,或者添加新功能

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class HPBook : public Computer  //Computer是组合关系示例代码中实现的类
    {
        string mOS;
    public:
        HPBook()
        {
            mOS = "Windows 8";
        }
        void install(string os)
        {
            mOS = os;
        }
        void OS()
        {
            cout << mOS << endl;
        }
    };
    
    class MacBook : public Computer
    {
    public:
        void OS()
        {
            cout << "Mac OS" << endl;
        }
    };
    
    int main()
    {
        HPBook hp;
    
        hp.power();
        hp.install("Ubuntu 16.04 LTS");
        hp.OS();
    
        cout << endl;
    
        MacBook mac;
    
        hp.power();
        mac.OS();
    
        cout << endl;
    
        return 0;
    }
    

    建议:作为类设计的一般原则,能用组合关系的,就不要用继承关系,前提是组合关系可以实现所需功能和较好的架构设计。

    2. 继承中的访问级别

    • 面向对象中的访问级别包括public、private和protected
    • protected是专门为了继承而设计的
    • protected成员变量不能被外界直接访问,但可以被子类直接访问
    • 在设计类的时候,需要根据具体需求来规划不同的访问级别

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Parent
    {
    protected:
        int mv;
    public:
        Parent()
        {
            mv = 100;
        }
    
        int value()
        {
            return mv;
        }
    };
    
    class Child : public Parent
    {
    public:
        int addValue(int v)
        {
            mv = mv + v;
        }
    };
    
    int main()
    {
        Parent p;
        Child c;
    
        // p.mv = 1000;   // error
        // c.mv = 10000;  // error
    
        c.addValue(50);
        cout << "c.mv = " << c.value() << endl;
    
        return 0;
    }
    

    上面的Demo简单地展示了protecded成员变量的特性和使用方式,下面再看一个复杂一些的综合示例,UML类图如下所示,
    Polint和Line都继承自Object,同时Line还组合使用了Point。

    #include <iostream>
    #include <sstream>
    #include <string>
    
    using namespace std;
    
    class Object
    {
    protected:
        string mName;
        string mInfo;
    public:
        Object()
        {
            mName = "Object";
            mInfo = "NULL";
        }
    
        string name()
        {
            return mName;
        }
    
        string info()
        {
            return mInfo;
        }
    };
    
    class Point : public Object
    {
    private:
        int mX;
        int mY;
    public:
        Point(int x = 0, int y = 0)
        {
            ostringstream s;
    
            mX = x;
            mY = y;
            mName = "Point";
    
            s << "P(" << mX << ", " << mY << ")";
    
            mInfo = s.str();
        }
    
        int x()
        {
            return mX;
        }
    
        int y()
        {
            return mY;
        }
    };
    
    class Line : public Object
    {
    private:
        Point mStart;
        Point mEnd;
    public:
        Line(Point start, Point end)
        {
            ostringstream s;
    
            mStart = start;
            mEnd = end;
            mName = "Line";
    
            s << "Line from " << mStart.info() << " to " << mEnd.info();
    
            mInfo = s.str();
        }
    
        Point &begin()
        {
            return mStart;
        }
    
        Point &end()
        {
            return mEnd;
        }
    };
    
    int main()
    {
        Object o;
    
        cout << o.name() << endl;
        cout << o.info() << endl;
        cout << endl;
    
        Point p(1, 2);
    
        cout << p.name() << endl;
        cout << p.info() << endl;
        cout << endl;
    
        Point start(3, 4);
        Point end(5, 6);
        Line l(start, end);
    
        cout << l.name() << endl;
        cout << l.info() << endl;
    
        return 0;
    }
    

    3. 不同的继承方式

    类似于成员变量有三种访问级别,C++也支持三种不同的继承方式

    • public继承:父类成员变量在子类中保持原有访问级别
    • private继承:父类成员变量在子类中全部变为private
    • protecded继承:父类public成员变量在子类中变为protected,其余成员变量访问级别保持不变

    虽然C++支持三种不同的继承方式,但private和protected继承带来的复杂性远大于实用性,因此在工程中一般推荐使用public继承
    实际上,C++的派生语言(如Java、C#)都只支持public继承这一种方式,也变相说明了这一点。

    4. 继承中的构造与析构

    父类和子类都可以定义构造函数,其中子类构造函数必须对继承而来的成员变量进行初始化,初始化的方法有两种:

    • 直接使用赋值的方式进行初始化,仅适用于父类public和protected成员
    • 调用父类构造函数进行初始化,这里也有两种调用方式
      • 隐式调用:适用于父类无参构造函数和默认参数构造函数
      • 显式调用:通过初始化列表进行调用,适用于所有父类构造函数

    继承中的构造与析构顺序,在“对象的构造与析构(二)”中已经讲过,不再赘述。

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Parent
    {
    protected:
        string ps;
        int mv;
    public:
        Parent()
        {
            cout << "Parent()" << endl;
            ps = "Default";
            mv = 0;
        }
    
        Parent(string s, int v)
        {
            cout << "Parent(string s, int v) : " << s << ", " << v << endl;
            ps = s;
            mv = v;
        }
    
        ~Parent()
        {
            cout << "~Parent() : " << ps << ", " << mv << endl;
        }
    };
    
    class Child : public Parent
    {
    private:
        string cs;
    public:
        /*
         * 进入Child()前,隐式调用Parent(),初始化父类成员;
         * 进入Child()后,使用赋值方式,初始化父类成员.
        */
        Child()
        {
            cout << "Child()" << endl;
            ps = "Parent Default";
            mv = 100;
            cs = "Default";
        }
    
        /*
         * 进入Child()前,使用初始化列表,显式调用Parent(string s, int v),初始化父类成员.
        */
        Child(string s) : Parent(s, 200)
        {
            cout << "Child(string s) : " << s << endl;
            cs = s;
        }
    
        ~Child()
        {
            cout << "~Child() : " << cs << endl;
        }
    };
    
    int main()
    {
        Child c1;
        Child c2("child");
    
        cout << endl;
    
        return 0;
    }
    

  • 相关阅读:
    字串变换
    重建道路
    poj3278 Catch That Cow
    机器人搬重物
    [HNOI2004]打鼹鼠
    曼哈顿距离
    邮票面值设计
    poj1101 The Game
    解决了一个堆破坏问题
    模型资源从无到有一条龙式体验
  • 原文地址:https://www.cnblogs.com/songhe364826110/p/11599269.html
Copyright © 2011-2022 走看看