类的继承复习(啥都不会了。。。)(三)

简介: 类的继承复习(啥都不会了。。。)

要使用虚拟析构函数:

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/1.jpg

不适当的代码将组织动态联编:

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/1633912209176117500.jpg

5.虚函数的工作原理

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/2.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/3.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/4.jpg

6.重新定义隐藏方法(虚方法参数列表不匹配时情况)

     如果派生类没有重新定义函数,将使用该函数的基类版本。如果派生类位于派生链中,则将使用最新的虚函数版本,例外的情况是基类版本是隐藏的。

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/5.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/6.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/7.jpg

7.protected成员与单设计模式

     protected与private相似,在类外只能用公有类成员来访问protected部分中的类成员。他们之间的区别只有在基类派生的类中才会表现出来。派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。因此,对于外部世界来说,保护成员的行为与私有成员相似;但对于派生类来说,保护成员的行为与公有成员相似。

     关于单设计模式:

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/8.jpg

https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/9.jpg

8.抽象基类

      abstract base class,ABC。当类声明中包含纯虚函数时, 则不能创建该类的对象。纯虚函数声明的结尾处为=0。包含纯虚函数的类只用作基类,要成为真正的ABC,必须至少包含一个纯虚函数。

view plaincopy to clipboardprint?
// acctabc.h  -- bank account classes   
#ifndef ACCTABC_H_   
#define ACCTABC_H_   
// Abstract Base Class   
class AcctABC   
{   
private:   
    enum {MAX = 35};   
    char fullName[MAX];   
    long acctNum;   
    double balance;   
protected:   
    const char * FullName() const {return fullName;}   
    long AcctNum() const {return acctNum;}   
    std::ios_base::fmtflags SetFormat() const;   
public:    
    AcctABC(const char *s = "Nullbody", long an = -1,   
                double bal = 0.0);   
    void Deposit(double amt) ;   
    virtual void Withdraw(double amt) = 0; // pure virtual function   
    double Balance() const {return balance;};   
    virtual void ViewAcct() const = 0;     // pure virtual function   
    virtual ~AcctABC() {}   
};   
// Brass Account Class   
class Brass :public AcctABC   
{   
public:   
    Brass(const char *s = "Nullbody", long an = -1,   
           double bal = 0.0) : AcctABC(s, an, bal) { }   
    virtual void Withdraw(double amt);   
    virtual void ViewAcct() const;   
    virtual ~Brass() {}   
};   
//Brass Plus Account Class   
class BrassPlus : public AcctABC   
{   
private:   
    double maxLoan;   
    double rate;   
    double owesBank;   
public:   
    BrassPlus(const char *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 ViewAcct()const;   
    virtual void Withdraw(double amt);   
    void ResetMax(double m) { maxLoan = m; }   
    void ResetRate(double r) { rate = r; };   
    void ResetOwes() { owesBank = 0; }   
};   
#endif  
// acctabc.h  -- bank account classes
#ifndef ACCTABC_H_
#define ACCTABC_H_
// Abstract Base Class
class AcctABC
{
private:
    enum {MAX = 35};
    char fullName[MAX];
    long acctNum;
    double balance;
protected:
    const char * FullName() const {return fullName;}
    long AcctNum() const {return acctNum;}
    std::ios_base::fmtflags SetFormat() const;
public: 
    AcctABC(const char *s = "Nullbody", long an = -1,
                double bal = 0.0);
    void Deposit(double amt) ;
    virtual void Withdraw(double amt) = 0; // pure virtual function
    double Balance() const {return balance;};
    virtual void ViewAcct() const = 0;     // pure virtual function
    virtual ~AcctABC() {}
};
// Brass Account Class
class Brass :public AcctABC
{
public:
    Brass(const char *s = "Nullbody", long an = -1,
           double bal = 0.0) : AcctABC(s, an, bal) { }
    virtual void Withdraw(double amt);
    virtual void ViewAcct() const;
    virtual ~Brass() {}
};
//Brass Plus Account Class
class BrassPlus : public AcctABC
{
private:
    double maxLoan;
    double rate;
    double owesBank;
public:
    BrassPlus(const char *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 ViewAcct()const;
    virtual void Withdraw(double amt);
    void ResetMax(double m) { maxLoan = m; }
    void ResetRate(double r) { rate = r; };
    void ResetOwes() { owesBank = 0; }
};
#endif
view plaincopy to clipboardprint?
// acctabc.cpp -- bank account class methods   
#include <iostream>   
#include <cstring>   
using std::cout;   
using std::ios_base;   
using std::endl;   
#include "acctabc.h"   
// Abstract Base Class   
AcctABC::AcctABC(const char *s, long an, double bal)   
{   
    std::strncpy(fullName, s, MAX - 1);   
    fullName[MAX - 1] = '\0';   
    acctNum = an;   
    balance = bal;   
}   
void AcctABC::Deposit(double amt)   
{   
    if (amt < 0)   
        cout << "Negative deposit not allowed; "  
             << "deposit is cancelled.\n";   
    else  
        balance += amt;   
}   
void AcctABC::Withdraw(double amt)   
{   
    balance -= amt;   
}   
// protected method   
ios_base::fmtflags AcctABC::SetFormat() const  
{   
 // set up ###.## format   
    ios_base::fmtflags initialState =    
        cout.setf(ios_base::fixed, ios_base::floatfield);   
    cout.setf(ios_base::showpoint);   
    cout.precision(2);   
    return initialState;    
}   
// Brass methods   
void Brass::Withdraw(double amt)   
{   
    if (amt < 0)   
        cout << "Withdrawal amount must be positive; "  
             << "withdrawal canceled.\n";   
    else if (amt <= Balance())   
        AcctABC::Withdraw(amt);   
    else  
        cout << "Withdrawal amount of $" << amt   
             << " exceeds your balance.\n"  
             << "Withdrawal canceled.\n";   
}   
void Brass::ViewAcct() const  
{   
    ios_base::fmtflags initialState = SetFormat();   
    cout << "Brass Client: " << FullName() << endl;   
    cout << "Account Number: " << AcctNum() << endl;   
    cout << "Balance: $" << Balance() << endl;   
    cout.setf(initialState);   
}   
// BrassPlus Methods   
BrassPlus::BrassPlus(const char *s, long an, double bal,   
           double ml, double r) : AcctABC(s, an, bal)   
{   
    maxLoan = ml;   
    owesBank = 0.0;   
    rate = r;    
}   
BrassPlus::BrassPlus(const Brass & ba, double ml, double r)   
           : AcctABC(ba)   // uses implicit copy constructor   
{   
    maxLoan = ml;   
    owesBank = 0.0;   
    rate = r;   
}   
void BrassPlus::ViewAcct() const  
{   
    ios_base::fmtflags initialState = SetFormat();   
    cout << "BrassPlus Client: " << FullName() << endl;   
    cout << "Account Number: " << AcctNum() << endl;   
    cout << "Balance: $" << Balance() << endl;   
    cout << "Maximum loan: $" << maxLoan << endl;   
    cout << "Owed to bank: $" << owesBank << endl;   
    cout << "Loan Rate: " << 100 * rate << "%\n";   
    cout.setf(initialState);   
}   
void BrassPlus::Withdraw(double amt)   
{   
    ios_base::fmtflags initialState = SetFormat();   
    double bal = Balance();   
    if (amt <= bal)   
        AcctABC::Withdraw(amt);   
    else if ( amt <= bal + maxLoan - owesBank)   
    {   
        double advance = amt - bal;   
        owesBank += advance * (1.0 + rate);   
        cout << "Bank advance: $" << advance << endl;   
        cout << "Finance charge: $" << advance * rate << endl;   
        Deposit(advance);   
        AcctABC::Withdraw(amt);   
    }   
    else  
        cout << "Credit limit exceeded. Transaction cancelled.\n";   
    cout.setf(initialState);    
}  
// acctabc.cpp -- bank account class methods
#include <iostream>
#include <cstring>
using std::cout;
using std::ios_base;
using std::endl;
#include "acctabc.h"
// Abstract Base Class
AcctABC::AcctABC(const char *s, long an, double bal)
{
    std::strncpy(fullName, s, MAX - 1);
    fullName[MAX - 1] = '\0';
    acctNum = an;
    balance = bal;
}
void AcctABC::Deposit(double amt)
{
    if (amt < 0)
        cout << "Negative deposit not allowed; "
             << "deposit is cancelled.\n";
    else
        balance += amt;
}
void AcctABC::Withdraw(double amt)
{
    balance -= amt;
}
// protected method
ios_base::fmtflags AcctABC::SetFormat() const
{
 // set up ###.## format
    ios_base::fmtflags initialState = 
        cout.setf(ios_base::fixed, ios_base::floatfield);
    cout.setf(ios_base::showpoint);
    cout.precision(2);
    return initialState; 
}
// Brass methods
void Brass::Withdraw(double amt)
{
    if (amt < 0)
        cout << "Withdrawal amount must be positive; "
             << "withdrawal canceled.\n";
    else if (amt <= Balance())
        AcctABC::Withdraw(amt);
    else
        cout << "Withdrawal amount of $" << amt
             << " exceeds your balance.\n"
             << "Withdrawal canceled.\n";
}
void Brass::ViewAcct() const
{
    ios_base::fmtflags initialState = SetFormat();
    cout << "Brass Client: " << FullName() << endl;
    cout << "Account Number: " << AcctNum() << endl;
    cout << "Balance: $" << Balance() << endl;
    cout.setf(initialState);
}
// BrassPlus Methods
BrassPlus::BrassPlus(const char *s, long an, double bal,
           double ml, double r) : AcctABC(s, an, bal)
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r; 
}
BrassPlus::BrassPlus(const Brass & ba, double ml, double r)
           : AcctABC(ba)   // uses implicit copy constructor
{
    maxLoan = ml;
    owesBank = 0.0;
    rate = r;
}
void BrassPlus::ViewAcct() const
{
    ios_base::fmtflags initialState = SetFormat();
    cout << "BrassPlus Client: " << FullName() << endl;
    cout << "Account Number: " << AcctNum() << endl;
    cout << "Balance: $" << Balance() << endl;
    cout << "Maximum loan: $" << maxLoan << endl;
    cout << "Owed to bank: $" << owesBank << endl;
    cout << "Loan Rate: " << 100 * rate << "%\n";
    cout.setf(initialState);
}
void BrassPlus::Withdraw(double amt)
{
    ios_base::fmtflags initialState = SetFormat();
    double bal = Balance();
    if (amt <= bal)
        AcctABC::Withdraw(amt);
    else if ( amt <= bal + maxLoan - owesBank)
    {
        double advance = amt - bal;
        owesBank += advance * (1.0 + rate);
        cout << "Bank advance: $" << advance << endl;
        cout << "Finance charge: $" << advance * rate << endl;
        Deposit(advance);
        AcctABC::Withdraw(amt);
    }
    else
        cout << "Credit limit exceeded. Transaction cancelled.\n";
    cout.setf(initialState); 
}
9.继承和动态内存分配
view plaincopy to clipboardprint?
//基类   
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);   
};  
//基类
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);
}; 

 如果基类使用动态内存分配,那么这将怎样影响派生类的实现呢。当派生类不使用new时,不需要为派生类定义显式析构函数,复制构造函数和赋值操作符。但是,当基类和派生类都采用动态内存分配时,派生类的析构函数,复制构造函数和赋值操作符都必须使用相应的基类方法来处理基类元素,这种要求是通过3种不同的方式来满足的。

view plaincopy to clipboardprint?
//派生类   
class hasDMA :public baseDMA   
{   
private:   
    char * style;   
public:   
    ...   
};  
//派生类
class hasDMA :public baseDMA
{
private:
    char * style;
public:
    ...
};

1)对于析构函数,这是自动完成的。派生类析构函数自动调用基类的析构函数,故其自身的职责是对派生类构造函数执行工作的进行清理。

view plaincopy to clipboardprint?
baseDMA::~baseDMA()   
{   
    delete [] label;   
}   
hasDMA::~hasDMA()   
{   
    delete [] style;   
}  
baseDMA::~baseDMA()
{
    delete [] label;
}
hasDMA::~hasDMA()
{
    delete [] style;
} 

2)对于构造函数,这是通过在初始化成员列表中调用基类的复制构造函数来完成的,如果不这样做,将自动调用基类的默认构造函数。

view plaincopy to clipboardprint?
hasDMA::hasDMA(const hasDMA & hs)   
         : baseDMA(hs)  // invoke base class copy constructor   
{   
    style = new char[std::strlen(hs.style) + 1];   
    std::strcpy(style, hs.style);   
}  
hasDMA::hasDMA(const hasDMA & hs)
         : baseDMA(hs)  // invoke base class copy constructor
{
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
}

hasDMA复制构造函数只能访问hasDMA的数据,因此它必须调用baseDMA复制构造函数来处理共享的baseDMA数据。没有参数类型为hasDMA引用的baseDMA构造函数,也不需要这样的构造函数。因为基类引用可以指向派生类型。

3)对于赋值操作符,这是通过使用作用域解析操作符显式地调用基类的赋值操作符来完成的。

view plaincopy to clipboardprint?
baseDMA & baseDMA::operator=(const baseDMA & rs)   
{   
    if (this == &rs)   
        return *this;   
    delete [] label;   
    label = new char[std::strlen(rs.label) + 1];   
    std::strcpy(label, rs.label);   
    rating = rs.rating;   
    return *this;   
}   
hasDMA & hasDMA::operator=(const hasDMA & hs)   
{   
    if (this == &hs)   
        return *this;   
    baseDMA::operator=(hs);  // copy base portion   
    style = new char[std::strlen(hs.style) + 1];   
    std::strcpy(style, hs.style);   
    return *this;   
}   
baseDMA & baseDMA::operator=(const baseDMA & rs)
{
    if (this == &rs)
        return *this;
    delete [] label;
    label = new char[std::strlen(rs.label) + 1];
    std::strcpy(label, rs.label);
    rating = rs.rating;
    return *this;
}
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
    if (this == &hs)
        return *this;
    baseDMA::operator=(hs);  // copy base portion
    style = new char[std::strlen(hs.style) + 1];
    std::strcpy(style, hs.style);
    return *this;
}
baseDMA::operator=(hs);  是函数表示法,相当于:  *this=hs;
10.派生类友元如何访问基类的友元
view plaincopy to clipboardprint?
class baseDMA   
{   
private:   
    ...       
public:   
    ...   
    friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs);   
};   
class hasDMA :public baseDMA   
{   
private:   
    ...   
public:   
    ...    
    friend std::ostream & operator<<(std::ostream & os,const hasDMA & rs);   
};  
class baseDMA
{
private:
    ...    
public:
    ...
    friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs);
};
class hasDMA :public baseDMA
{
private:
    ...
public:
    ... 
    friend std::ostream & operator<<(std::ostream & os,const hasDMA & rs);
};
view plaincopy to clipboardprint?
std::ostream & operator<<(std::ostream & os,const baseDMA & rs)   
{   
    os << "Label: " << rs.label << std::endl;   
    os << "Rating: " << rs.rating << std::endl;   
    return os;   
}   
std::ostream & operator<<(std::ostream & os,const hasDMA & hs)   
{   
    os << (const baseDMA &) hs;   
    os << "Style: " << hs.style << std::endl;   
    return os;   
}  
std::ostream & operator<<(std::ostream & os,const baseDMA & rs)
{
    os << "Label: " << rs.label << std::endl;
    os << "Rating: " << rs.rating << std::endl;
    return os;
}
std::ostream & operator<<(std::ostream & os,const hasDMA & hs)
{
    os << (const baseDMA &) hs;
    os << "Style: " << hs.style << std::endl;
    return os;
}

   因为友元不是成员函数,所以不能使用作用域解析操作符来指出要使用哪个函数。解决方法是使用强制类型转换,以便匹配原型时能够选择正确的函数。

11. 类方法返回对象还是返回引用

      在编码方面,直接返回对象与返回引用之间唯一的区别在于函数原型和函数头:

view plaincopy to clipboardprint?
Star nova1(const Star &);   //returns a Star object   
Star & nova2(const Star &);  //returns a reference to a Star  
Star nova1(const Star &);   //returns a Star object

直接返回对象与按值传递对象相似:它们都生成临时拷贝。同样,返回引用与按引用传递对象相似:调用和被调用的函数对同一个对象进行操作。大部分情况下,返回引用可节省时间和内存,不过并不可以总是返回引用,函数不能返回在函数中创建的临时对象的引用。通用的规则是:如果函数返回在函数中创建的临时对象,则不要使用引用。如果函数返回的是通过引用或指针传递给它的对象,则应按引用返回对象。


相关文章
|
存储 编译器 数据处理
c++学习之类与对象3
c++学习之类与对象3
115 0
|
4月前
|
存储 编译器 数据安全/隐私保护
|
5月前
|
C++
C++继承的相关知识点
C++继承的相关知识点
29 0
|
6月前
|
存储 设计模式
继承和多态中的常见笔试面试题(一)
继承和多态中的常见笔试面试题(一)
|
6月前
|
C++
C++零基础教程(类的继承)
C++零基础教程(类的继承)
72 0
类的继承学习记录
类的继承学习记录
68 0
类的继承学习记录
|
存储 编译器 C++
【C++知识点】多态
【C++知识点】多态
89 0
|
C++
【C++知识点】继承
【C++知识点】继承
66 0
|
编译器 程序员 C++
【C++】继承知识点总结
一. 继承的概念及定义 比如我们要定义学生类(Student)和老师类(Teacher),作为人这两个类共有的基本属性包括姓名,年龄等。写两个类就要各自都声明姓名和年龄这两个成员变量,能不能单独写一个Person类里面只有姓名和年龄这两个成员变量,让学生类和老师类去继承Person类,这样就不用单独地再去声明姓名和年龄了。
|
编译器 C语言 C++
【C++】类与对象理解和学习(下)
【C++】类与对象理解和学习(下)
101 0