zoukankan      html  css  js  c++  java
  • Cpp Chapter 13: Class Inheritance Part2

    Noteworthy:
    1 constructors of the derived class use member initializer list syntax to initialize base class private members:

    BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : Brass(s,an,bal)
    {
        maxLoan = ml;
        owesBank = 0.0;
        rate = r;
    }
    

    2 A derived class method can call a base-class method using scope-resolution operator:

    BrassPlus::ViewAcct() const
    {
        Brass::ViewAcct();
        cout << "Maximum loan: $" << maxLoan << endl;
        cout << "Owed to bank: $" << owesBank << endl;
        cout << "Loan Rate: " << 100 * rate << "%
    ;
    }
    

    3 A derived class method can't access base class public members, but can use base class methods to achieve same result:

    void BrassPlus::Withdraw(amt)
    {
        double bal = Balance(); // use base class method Balance() to access the money in the bank
        ...
    }
    

    Next comes the code that uses both classes:

    // brass.h -- bank account classes
    #ifndef BRASS_H_INCLUDED
    #define BRASS_H_INCLUDED
    #include <string>
    
    class Brass
    {
    private:
        std::string fullName;
        long acctNum;
        double balance;
    public:
        Brass(const std::string & s = "Nullbody", long an = -1, double bal = 0.0);
        void Deposit(double amt);
        virtual void Withdraw(double amt);
        double Balance() const;
        virtual void ViewAcct() const;
        virtual ~Brass(){}
    };
    
    class BrassPlus : public Brass
    {
    private:
        double maxLoan;
        double rate;
        double owesBank;
    public:
        BrassPlus(const std::string & s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125);
        BrassPlus(const Brass & ba, double ml = 500, double r = 0.11125);
        virtual void ViewAcct() const;
        virtual void Withdraw(double amt);
        void ResetMax(double m) { maxLoan = m;};
        void ResetRate(double r) {rate = r;};
        void ResetOwes() { owesBank = 0;}
    };
    
    #endif // BRASS_H_INCLUDED
    

    In order to handle a mixture of Brass and BrassPlus objects, it is best to have an array of pointers-to-Brass to store them due to the polymorphic feature that base class pointers and references could point to both base class and derived class objects. This is where virtual methods came to be important:

    // usebrass2.cpp -- polymorphic example
    // compile with brass.cpp
    #include <iostream>
    #include <string>
    #include "brass.h"
    const int CLIENTS = 4;
    
    int main()
    {
        using std::cin;
        using std::cout;
        using std::endl;
    
        Brass * p_clients[CLIENTS];
        std::string temp;
        long tempnum;
        double tempbal;
        char kind;
    
        for (int i = 0; i < CLIENTS; i++)
        {
            cout << "Enter client's name: ";
            getline(cin, temp);
            cout << "Enter client's account number: ";
            cin >> tempnum;
            cout << "Enter opening balance: $";
            cin >> tempbal;
            cout << "Enter 1 for Brass Account or 2 for BrassPlus Account: ";
            while (cin >> kind && (kind != '1' && kind != '2'))
                cout << "Enter either 1 or 2: ";
            if (kind == '1')
                p_clients[i] = new Brass(temp,tempnum,tempbal);
            else
            {
                double tmax, trate;
                cout << "Enter the overdraft limit: $";
                cin >> tmax;
                cout << "Enter the interest rate as a decimal fraction: ";
                cin >> trate;
                p_clients[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
            }
            while (cin.get() != '
    ')
                continue;
        }
        cout << endl;
        for (int i = 0; i < CLIENTS; i++)
        {
            p_clients[i]->ViewAcct();
            cout << endl;
        }
        for (int i = 0; i < CLIENTS; i++)
        {
            delete p_clients[i];
        }
        cout << "Done.
    ";
        return 0;
    }
    

    Note worthy:
    The base class declared virtual destructor:

    virtual Brass::~Brass();
    

    In the same pointer example above, if it is not virtual, then delete p_clients[i] would invoke Brass::~Brass() due to the pointer type, leaving the derived class members undeleted. After adding the virtual, the delete p_clients[i] would invoke BrassPlus::~BrassPlus() first, which automatically invokes Brass::~Brass() when it terminates, ensuring the correct sequence of destructor calling.
    Thus it is common practice to declare methods derived and destructors as virtual methods.


    13.4 Static and dynamic binding

    ) binding
    binding: interpreting the function call as executing a particular block of function code
    static binding: binding that takes place during compilation
    dynamic binding: binding that takes place during runtime, example: virtual functions binding
    )
    upcasting: converting a derived-class reference or pointer to a base-class reference or pointer, always allowed without explicit type cast
    downcasting: converting a base-class pointer or reference to a derived-class pointer or reference, not allowed without explicit type cast
    When pointers/reference/pass-by-value argument passing:

    void fr(Brass & rb); // uses rb.ViewAcct()
    void fp(Brass * pb); // uses pb->ViewAcct()
    void fv(Brass b); // uses b.ViewAcct()
    int main()
    {
        BrassPlus b("Billy Bee", 123432, 10000.0);
        fr(b); // uses BrassPlus::ViewAcct()
        fp(b); // uses BrassPlus::ViewAcct()
        fv(b); // uses Brass::ViewAcct() !
    }
    

    Noteworthy that, passing by value only pass the Brass component of a BrassPlus object to the function, thus calling Brass::ViewAcct() eventually
    ) In short,
    the compiler uses static binding for non-virtual methods and dynamic binding for virtual methods
    Why static binding as default: 1 efficiency 2 use 'virtual' to mark methods to be redefined
    ) the way that virtual functions work:
    1 each object has its size increased to store address
    2 for each class, the compiler creates a table of addresses of virtual functions
    3 each function call has an extra step of going to a table to look up an address
    ) While redefining:
    Redefined inherited method should match the original prototype exactly, however, the return type may vart, termed covariance of return type
    If a method is overloaded in base class, redefine all versions in the derived class, or the undefined versions would be hided


    13.5 access control: protected

    The keyword protected is between private and public:
    1 Same as private in the outside world could only access protected members through public member functions
    2 Same as public in that for members of a derived class, they could access protected base-class members directly


    13.6 abstract base classes

    Suppose you are about to have a ellipse class and a circle class, you could abstract features that both classes have in common to form an abstract base class, short for ABC. For instance, they both have coordinates of center, a Move() method, an Area() that calculates the area. However, without sufficient data you couldn't give definitions of the Area() method, so C++ uses pure virtual function to provide an unimplemented function by adding = 0 at the end of its declaration:

    class BaseEllipse // abstract base class
    {
    private:
        double x;
        double y;
        ...
    public:
        BaseEllipse(double x0 = 0, double y0 = 0) : x(x0), y(y0) {}
        virtual ~BaseEllipse() {}
        void Move(int nx, ny) { x = nx; y = ny; }
        virtual double Area() const = 0; // pure virtual function
        ...
    }
    

    When a class contains one pure virtual function, you can't create an object of that class. For a class to be an ABC, it has to have at least one pure virtual function, by adding =0 to the end of the method's declaration. In contrast, classes like Circle and Ellipse are termed concrete classes. Classes derived from an ABC use regular virtual functions to implement the interface in terms of the properties of the particular derived class
    Rewrite the bank example using AcctABC class as ABC in this example:

    // acctabc.h // bank account classes
    #ifndef ACCTABC_H_INCLUDED
    #define ACCTABC_H_INCLUDED
    #include <iostream>
    #include <string>
    
    class AcctABC
    {
    private:
        std::string fullName;
        long acctNum;
        double balance;
    protected:
        struct Formatting
        {
            std::ios_base::fmtflags flag;
            std::streamsize pr;
        };
        const std::string & FullName() const {return fullName;}
        long AcctNum() const {return acctNum;}
        Formatting setFormat() const;
        void Restore(Formatting & f) const;
    public:
        AcctABC(const std::string & s = "Nullbody", long an = -1, double bal = 0.0);
        void Deposit(double amt);
        virtual void Withdraw(double amt) = 0; // pure virtual function, because the implementation varies between derived classes
        double Balance() const {return balance;};
        virtual void ViewAcct() const = 0; // pure virtual function, because the implementation varies between derived classes
        virtual ~AcctABC() {} // always make base-class destructor virtual
    };
    
    class Brass : public AcctABC
    {
    public:
        Brass(const std::string & s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s,an,bal) { }
        virtual void Withdraw(double amt);
        virtual void ViewAcct() const;
        virtual ~Brass() { };
    };
    
    class BrassPlus : public AcctABC
    {
    private:
        double maxLoan;
        double rate;
        double owesBank;
    public:
        BrassPlus(const std::string & s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10);
        BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
        virtual void Withdraw(double amt);
        virtual void ViewAcct() const;
        void ResetMax(double m) { maxLoan = m;}
        void ResetRate(double r) { rate = r;}
        void ResetOwes() { owesBank = 0;}
    };
    
    #endif // ACCTABC_H_INCLUDED
    
    // acctabc.h // bank account classes
    #ifndef ACCTABC_H_INCLUDED
    #define ACCTABC_H_INCLUDED
    #include <iostream>
    #include <string>
    
    class AcctABC
    {
    private:
        std::string fullName;
        long acctNum;
        double balance;
    protected:
        struct Formatting
        {
            std::ios_base::fmtflags flag;
            std::streamsize pr;
        };
        const std::string & FullName() const {return fullName;}
        long AcctNum() const {return acctNum;}
        Formatting setFormat() const;
        void Restore(Formatting & f) const;
    public:
        AcctABC(const std::string & s = "Nullbody", long an = -1, double bal = 0.0);
        void Deposit(double amt);
        virtual void Withdraw(double amt) = 0; // pure virtual function, because the implementation varies between derived classes
        double Balance() const {return balance;};
        virtual void ViewAcct() const = 0; // pure virtual function, because the implementation varies between derived classes
        virtual ~AcctABC() {} // always make base-class destructor virtual
    };
    
    class Brass : public AcctABC
    {
    public:
        Brass(const std::string & s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s,an,bal) { }
        virtual void Withdraw(double amt);
        virtual void ViewAcct() const;
        virtual ~Brass() { };
    };
    
    class BrassPlus : public AcctABC
    {
    private:
        double maxLoan;
        double rate;
        double owesBank;
    public:
        BrassPlus(const std::string & s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10);
        BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
        virtual void Withdraw(double amt);
        virtual void ViewAcct() const;
        void ResetMax(double m) { maxLoan = m;}
        void ResetRate(double r) { rate = r;}
        void ResetOwes() { owesBank = 0;}
    };
    
    #endif // ACCTABC_H_INCLUDED
    
    // usebrass3.cpp -- polymorphic example using an ABC
    // compile with brass.cpp
    #include <iostream>
    #include <string>
    #include "acctabc.h"
    const int CLIENTS = 4;
    
    int main()
    {
        using std::cin;
        using std::cout;
        using std::endl;
    
        AcctABC * p_clients[CLIENTS];
        std::string temp;
        long tempnum;
        double tempbal;
        char kind;
    
        for (int i = 0; i < CLIENTS; i++)
        {
            cout << "Enter client's name: ";
            getline(cin, temp);
            cout << "Enter client's account number: ";
            cin >> tempnum;
            cout << "Enter opening balance: $";
            cin >> tempbal;
            cout << "Enter 1 for Brass Account or 2 for BrassPlus Account: ";
            while (cin >> kind && (kind != '1' && kind != '2'))
                cout << "Enter either 1 or 2: ";
            if (kind == '1')
                p_clients[i] = new Brass(temp,tempnum,tempbal);
            else
            {
                double tmax, trate;
                cout << "Enter the overdraft limit: $";
                cin >> tmax;
                cout << "Enter the interest rate as a decimal fraction: ";
                cin >> trate;
                p_clients[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
            }
            while (cin.get() != '
    ')
                continue;
        }
        cout << endl;
        for (int i = 0; i < CLIENTS; i++)
        {
            p_clients[i]->ViewAcct();
            cout << endl;
        }
        for (int i = 0; i < CLIENTS; i++)
        {
            delete p_clients[i];
        }
        cout << "Done.
    ";
        return 0;
    }
    

    13.7 inheritance and dynamic memory allocation

    ) Case 1: derived class doesn't use new
    Given a base class using new, which defined explicit destructors and copy constructors, and overloaded the assignment operator. The derived class doesn't use new, so no more copy constructor/destructor/assignment overloading is required. (The default ones for derived class does memberwise copy, which uses the base class's redefined ones at the end. So does destructor)
    ) Case 2: derived class use new
    Given the definition:

    // Base class
    class baseDMA
    {
    private:
        char * label;
        int rating;
    public:
        baseDMA(const char * l = "null", int r = 0);
        baseDMA(const baseDMA & rs);
        virtual ~baseDMA();
        baseDMA & operator=(const baseDMA & rs);
    };
    
    // derived class
    class hasDMA : public baseDMA
    {
    private:
        char * style; // use new in costructors
    public:
        ...
    };
    

    In this case, you have to define copy constructor, explicit destructor and assignment operator for the derived class:
    1 explicit destructor
    It only needs to free up the memory dynamically allocated by the derived class, leaving memory allocated by base class to be freed by base class destructor:

    baseDMA::~baseDMA()
    {
        delete [] label;
    }
    hasDMA::~hasDMA()
    {
        delete [] style; // takes care of the hasDMA stuff
    }
    

    2 copy constructor
    use member initializer list to copy base-class members, and deep copy the derived-class members by itself:

    baseDMA::baseDMA(const baseDMA & rs)
    {
        label = new char[std::strlen(rs.label) + 1];
        std::strcpy(label, rs.label);
        rating = rs.rating;
    }
    hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) // copy the base-class members
    {
        style = new char[std::strlen(hs.style) + 1];
        std::strcpy(style, hs.style);
    }
    

    3 assignment operator
    let base assignment operator copy base portion and copy derived portion itself:

    hasDMA & hasDMA::operator=(const hasDMA & hs)
    {
        if (this = &hs)
            return *this;
        baseDMA::operator=(hs); // copy base portion
        delete [] style;
        style = new char[std::strlen(hs.style)+1];
        std::strcpy(style, hs.style);
        return *this;
    }
    

    Here comes code with class inheritance using dynamic memory allocation:

    // dma.h -- inheritance and dynamic memory allocation
    #ifndef DMA_H_INCLUDED
    #define DMA_H_INCLUDED
    #include <iostream>
    
    class baseDMA
    {
    private:
        char * label;
        int rating;
    public:
        baseDMA(const char * l = "null", int r = 0);
        baseDMA(const baseDMA & rs);
        virtual ~baseDMA();
        baseDMA & operator=(const baseDMA & rs);
        friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs);
    };
    
    class lacksDMA : private baseDMA
    {
    private:
        enum { COL_LEN = 40};
        char color[COL_LEN];
    public:
        lacksDMA(const char * c = "blank", const char * l = "null", int r = 0);
        lacksDMA(const char * c, const baseDMA & rs);
        friend std::ostream & operator<<(std::ostream & os, const lacksDMA & rs);
    };
    
    class hasDMA : public baseDMA
    {
    private:
        char * style;
    public:
        hasDMA(const char * s = "none", const char * l = "null", int r = 0);
        hasDMA(const char * s, const baseDMA & rs);
        hasDMA(const hasDMA & hs);
        ~hasDMA();
        hasDMA & operator=(const hasDMA & rs);
        friend std::ostream & operator<<(std::ostream & os, const hasDMA & rs);
    };
    
    
    #endif // DMA_H_INCLUDED
    
    // dma.cpp -- dma class methods
    
    #include "dma.h"
    #include <cstring>
    
    baseDMA::baseDMA(const char * l, int r)
    {
        label = new char[std::strlen(l)+1];
        strcpy(label, l);
        rating = r;
    }
    
    baseDMA::baseDMA(const baseDMA & rs)
    {
        label = new char[std::strlen(rs.label) + 1];
        strcpy(label, rs.label);
        rating = rs.rating;
    }
    
    baseDMA::~baseDMA()
    {
        delete [] label;
    }
    
    baseDMA & baseDMA::operator=(const baseDMA & rs)
    {
        if (this == &rs)
            return *this;
        delete [] label;
        label = new char[std::strlen(rs.label) + 1];
        strcpy(label, rs.label);
        rating = rs.rating;
        return *this;
    }
    
    std::ostream & operator<<(std::ostream & os, const baseDMA & rs)
    {
        os << "Label: " << rs.label << std::endl;
        os << "Rating: " << rs.rating << std::endl;
        return os;
    }
    
    lacksDMA::lacksDMA(const char * c, const char * l, int r) : baseDMA(l, r)
    {
        strncpy(color, c, 39);
        color[39] = '';
    }
    
    lacksDMA::lacksDMA(const char * c, const baseDMA & rs) : baseDMA(rs)
    {
        strncpy(color, c, COL_LEN - 1);
        color[COL_LEN - 1] = '';
    }
    
    std::ostream & operator<<(std::ostream & os, const lacksDMA & ls)
    {
        os << (const baseDMA &) ls;
        os << "Color: " << ls.color << std::endl;
        return os;
    }
    
    hasDMA::hasDMA(const char * s, const char * l, int r) : baseDMA(l, r)
    {
        style = new char[std::strlen(s) + 1];
        std::strcpy(style, s);
    }
    
    hasDMA::hasDMA(const char * s, const baseDMA & rs) : baseDMA(rs)
    {
        style = new char[std::strlen(s) + 1];
        std::strcpy(style, s);
    }
    
    hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs)
    {
        style = new char[std::strlen(hs.style) + 1];
        strcpy(style, hs.style);
    }
    
    hasDMA::~hasDMA()
    {
        delete [] style;
    }
    
    hasDMA & hasDMA::operator=(const hasDMA & hs)
    {
        if (this == &hs)
            return *this;
        baseDMA::operator=(hs);
        delete [] style;
        style = new char[std::strlen(hs.style) + 1];
        strcpy(style, hs.style);
        return *this;
    }
    
    std::ostream & operator<<(std::ostream & os, const hasDMA & hs)
    {
        os << (const baseDMA &) hs;
        os << "Style: " << hs.style << std::endl;
        return os;
    }
    
    // usedma.cpp -- inheritance, friends, and DMA
    // compile with dma.cpp
    #include <iostream>
    #include "dma.h"
    int main()
    {
        using std::endl;
        using std::cout;
    
        baseDMA shirt("Portabelly", 8);
        lacksDMA balloon("red", "Blimpo", 4);
        hasDMA map("Mercator", "Buffalo Keys", 5);
        cout << "Displaying baseDMA object:
    ";
        cout << shirt << endl;
        cout << "Displaying lacksDMA object:
    ";
        cout << balloon << endl;
        cout << "Displaying hasDMA object:
    ";
        cout << map << endl;
        lacksDMA balloon2(balloon);
        cout << "Result of lacksDMA copy:
    ";
        cout << balloon2 << endl;
        hasDMA map2;
        map2 = map;
        cout << "Result of hasDMA assignment:
    ";
        cout << map2 << endl;
        return 0;
    }
    
  • 相关阅读:
    MQTT---HiveMQ源代码具体解释(四)插件载入
    HTTP访问控制(CORS)
    java实现限流
    Orika的使用姿势,Orika java bean copy 框架
    uploadify是通过flash上传,服务器获取type为application/octet-stream
    ThinkPHP 5使用 Composer 组件名称可以从https://packagist.org/ 搜索到
    3种方法轻松处理php开发中emoji表情的问题
    解决thinkphp设置session周期无效的问题
    如何使用沙箱测试单笔转账到支付宝账号(php版) https://openclub.alipay.com/read.php?tid=1770&fid=28
    微信jssdk批量添加卡券接口
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/9835676.html
Copyright © 2011-2022 走看看