c++对象和类(3) - 继承

2018-07-08 15:42:15
class Shape 
{
   public:
      void setWidth(int w){width = w;}
};

//其他语言的默认继承
class Rectangle: public Shape{
    public:
        //先通过成员初始化列表调用父类的构造函数Shape
        //然后调用 Rectangle 构造函数
        Rectangle(int a,int b,int c):Shape(c,b){
            
        }
}

//把Shape的public,protected成员都变为protected
class Rectangle: protected Shape{};

//把Shape的所有成员都变为private
class Rectangle: private Shape{};
    
//[多继承]
class Rectangle: public Shape, public PaintCost{};

有关派生类构造函数的要点如下:

1.首先创建基类
2.派生类构造函数通过成员初始化列表将信息传递给基类构造函数
3.派生类构造函数应初始化派生类新增的数据成员

如果不使用成员初始化列表来调用基类构造函数,那么基类的默认构造函数将会被调用。

重写成员函数,虚函数

//重写成员函数
class father{
    void foo(){count << "111";}
}
class child:public father{
    void foo(){count << "222";}         //重写成员函数
}
father *obj_1 = new child();
obj_1->foo();                           //"111"
class father{
    virtual void foo(){count << "111";}
}
class child:public father{
    void foo(){count << "222";}             //重写虚函数
}
father *obj_1 = new child();
obj_1->foo();                           //"222"--多态
//[虚函数]
virtual void foo(){cout << "333"}

//[纯虚函数]
virtual void foo() = 0;

使用虚函数时,在内存和执行速度方面有一定的成本,包括: 

1.每个对象都将增大,增大量为存储地址的空间;
2.对于每个类,编译器都创建一个虚函数地址表(数组);
3.对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址

虽然非虚函数的效率比虚函数稍高,但不具备动态联编功能。

我们已经讨论了虚函数的一些要点。

1.在基类方法的声明中使用关键字 virtual 可使该方法在基类以及所有的派生类(包括从派生类派生出来的类)中是虚的。
2.如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法,而不使用为引用或指针类型定义的方法。这称为动态联编或晚期联编。这种行为非常重要,因为这样基类指针或引用可以指向派生类对象。
3.如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚的。


抽象类

1.有1个纯虚函数的类就是抽象类

2.抽象类只能被继承,并在子类中实现


多重继承

下面我们看一个例子,我们定义抽象基类 Worker,使它派生出 Waiter 类和 Singer 类,然后从 Waiter 类和 Singer 类派生出 SingingWaiter类。

// worker.h
#ifndef WORKER0_H_
#define WORKER0_H_

#include <string>

class Worker    // an abstract base class
{
    private:
        std::string fullname;
        long id;
    public:
        Worker() : fullname("no one"), id(0L) {}
        Worker(const std::string & s, long n)
                : fullname(s), id(n) {}
        virtual ~Worker() = 0;      // pure virtual destructor
        virtual void Set();
        virtual void Show() const;
};

class Waiter : public Worker
{
    private:
        int panache;
    public:
        Waiter() : Worker(), panache(0) {}
        Waiter(const std::string & s, long n, int p = 0)
                : Worker(s, n), panache(p) {}
        Waiter(const Worker & wk, int p = 0)
                : Worker(wk), panache(p) {}
        void Set();
        void Show() const;
};

class Singer : public Worker
{
    protected:
        enum {other, alto, contralto, soprano,
                        bass, baritone, tenor};
        enum {Vtypes = 7};
    private:
        static char * pv[Vtypes];   // string equivs of voice types
        int voice;
    public:
        Singer() : Worker(), voice(other) {}
        Singer(const std::string & s, long n, int v = other)
                : Worker(s, n), voice(v) {}
        Singer(const Worker & wk, int v = other)
                : Worker(wk), voice(v) {}
        void Set();
        void Show() const;
};

#endif
// worker.cpp
#include "worker0.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
// Worker methods

// must implement virtual destructor, even if pure
Worker::~Worker() {}

void Worker::Set()
{
    cout << "Enter worker's name: ";
    getline(cin, fullname);
    cout << "Enter worker's ID: ";
    cin >> id;
    while (cin.get() != '\n')
        continue;
}

void Worker::Show() const
{
    cout << "Name: " << fullname << "\n";
    cout << "Employee ID: " << id << "\n";
}

// Waiter methods
void Waiter::Set()
{
    Worker::Set();
    cout << "Enter waiter's panache rating: ";
    cin >> panache;
    while (cin.get() != '\n')
        continue;
}

void Waiter::Show() const
{
    cout << "Category: waiter\n";
    Worker::Show();
    cout << "Panache rating: " << panache << "\n";
}

// Singer methods
char * Singer::pv[] = {"other", "alto", "contralto",
            "soprano", "bass", "baritone", "tenor"};

void Singer::Set()
{
    Worker::Set();
    cout << "Enter number for singer's vocal range:\n";
    int i;
    for (i = 0; i < Vtypes; i++)
    {
        cout << i << ": " << pv[i] << "   ";
        if ( i % 4 == 3)
            cout << endl;
    }
    if (i % 4 != 0)
        cout << endl;
    while (cin >> voice && (voice < 0 || voice >= Vtypes) )
        cout << "Please enter a value >= 0 and < " << Vtypes << endl;

    while (cin.get() != '\n')
        continue;
}

void Singer::Show() const
{
    cout << "Category: singer\n";
    Worker::Show();
    cout << "Vocal range: " << pv[voice] << endl;
}
// worktest.cpp
#include <iostream>
#include "worker0.h"
const int LIM = 4;
int main()
{
    Waiter bob("Bob Apple", 314L, 5);
    Singer bev("Beverly Hills", 522L, 3);
    Waiter w_temp;
    Singer s_temp;

    Worker * pw[LIM] = {&bob, &bev, &w_temp, &s_temp};

    int i;
    for (i = 2; i < LIM; i++)
        pw[i]->Set();
    for (i = 0; i < LIM; i++)
    {
        pw[i]->Show();
        std::cout << std::endl;
    }

    return 0;
}

上面是没有问题的,但是如果有 SingingWaiter 从 Singer和Waiter类派生出后,问题就来了,此时有2个Sorker。

class SingingWaiter: public Singer, public Waiter {...};
SingingWaiter ed;
Worker * pw = &ed; // 出错,不知道是哪一个

#可以利用类的类型转换
Worker * pw1 = (Waiter *) &ed; // the Worker in Waiter
Worker * pw2 = (Singer *) &ed; // the Worker in Singer

上面这种做法会变得很复杂。

c++引入多重继承的同时,引入了新技术 - 虚基类

class Singer : virtual public Worker {...};
class Waiter : public virtual Worker {...};
class SingingWaiter: public Singer, public Waiter {...};

此时,Singer和Waiter共用一个Worker副本。

新的构造规则

class A
{
    int a;
public:
    A(int n = 0) : a(n) {}
    ...
};
class B: public A
{
    int b;
public:
    B(int m = 0, int n = 0) : A(n), b(m) {}
    ...
};
class C : public B
{
    int c;
public:
    C(int q = 0, int m = 0, int n = 0) : B(m, n), c(q) {}
    ...
};

C类的构造函数只能调用B类的构造函数,而B类的构造函数只能调用A类的构造函数。这里,C类的构造函数使用值q,并将值m和n传递给B类的构造函数,而B类的构造函数使用值m,并将值n传递给A类的构造函数。

如果 Worker 是虚基类,则这种信息自动传递将不起作用。例如,对于下面构造函数:

SingingWaiter(const Worker & wk, int p = 0, int v = Singer::other)
: Waiter(wk,p), Singer(wk,v) {} // 不允许

上面有2条途径将 wk 传递给Worker,一个是通过 Waiter,一个是通过 Singer,所以编译器确定不了,所以静止这样写,不过可以显式的指定。

SingingWaiter(const Worker & wk, int p = 0, int v = Singer::other)
: Worker(wk), Waiter(wk,p), Singer(wk,v) {}


使用哪个方法

假设没有在 SingingWaiter 中定义Show方法,调用 Show() 方法时,到底调用哪个 Show() 方法。

对于单继承,如果没有重新定义 Show(),则使用最近祖先中的定义。

多继承可以利用作用域解析运算符

SingingWaiter newhire("Elise Hawks", 2005, 6, soprano);
newhire.Singer::Show(); // use Singer version

当然,更好的办法是在 SingingWaiter 中重新定义 Show()

void SingingWaiter::Show(){
    Singer::Show();
}
// workermi.h
#ifndef WORKERMI_H_
#define WORKERMI_H_

#include <string>

class Worker    // an abstract base class
{
    private:
        std::string fullname;
        long id;
    protected:
        virtual void Data() const;
        virtual void Get();
    public:
        Worker() : fullname("no one"), id(0L) {}
        Worker(const std::string & s, long n)
                : fullname(s), id(n) {}
        virtual ~Worker() = 0;  // pure virtual function
        virtual void Set() = 0;
        virtual void Show() const = 0;
};

class Waiter : virtual public Worker
{
    private:
        int panache;
    protected:
        void Data() const;
        void Get();
    public:
        Waiter() : Worker(), panache(0) {}
        Waiter(const std::string & s, long n, int p = 0)
                : Worker(s, n), panache(p) {}
        Waiter(const Worker & wk, int p = 0)
                : Worker(wk), panache(p) {}
        void Set();
        void Show() const;
};

class Singer : virtual public Worker
{
    protected:
        enum {other, alto, contralto, soprano,
                            bass, baritone, tenor};
        enum {Vtypes = 7};
        void Data() const;
        void Get();
    private:
        static char *pv[Vtypes];    // string equivs of voice types
        int voice;
    public:
        Singer() : Worker(), voice(other) {}
        Singer(const std::string & s, long n, int v = other)
                : Worker(s, n), voice(v) {}
        Singer(const Worker & wk, int v = other)
                : Worker(wk), voice(v) {}
        void Set();
        void Show() const;
};

// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
    protected:
        void Data() const;
        void Get();
    public:
        SingingWaiter() {}
        SingingWaiter(const std::string & s, long n, int p = 0,
                                int v = other)
                : Worker(s,n), Waiter(s, n, p), Singer(s, n, v) {}
        SingingWaiter(const Worker & wk, int p = 0, int v = other)
                : Worker(wk), Waiter(wk, p), Singer(wk, v) {}
        SingingWaiter(const Waiter & wt, int v = other)
                : Worker(wt), Waiter(wt), Singer(wt, v) {}
        SingingWaiter(const Singer & wt, int p = 0)
                : Worker(wt), Waiter(wt, p), Singer(wt) {}
        void Set();
        void Show() const;
};

#endif
// workermi.cpp
#include "workermi.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
// Worker methods
Worker::~Worker() { }

// protected methods
void Worker::Data() const
{
    cout << "Name: " << fullname << endl;
    cout << "Employee ID: " << id << endl;
}

void Worker::Get()
{
    getline(cin, fullname);
    cout << "Enter worker's ID: ";
    cin >> id;
    while (cin.get() != '\n')
        continue;
}

// Waiter methods
void Waiter::Set()
{
    cout << "Enter waiter's name: ";
    Worker::Get();
    Get();
}

void Waiter::Show() const
{
    cout << "Category: waiter\n";
    Worker::Data();
    Data();
}

// protected methods
void Waiter::Data() const
{
    cout << "Panache rating: " << panache << endl;
}
void Waiter::Get()
{
    cout << "Enter waiter's panache rating: ";
    cin >> panache;
    while (cin.get() != '\n')
        continue;
}

// Singer methods

char * Singer::pv[Singer::Vtypes] = {"other", "alto", "contralto",
            "soprano", "bass", "baritone", "tenor"};

void Singer::Set()
{
    cout << "Enter singer's name: ";
    Worker::Get();
    Get();
}

void Singer::Show() const
{
    cout << "Category: singer\n";
    Worker::Data();
    Data();
}

// protected methods
void Singer::Data() const
{
    cout << "Vocal range: " << pv[voice] << endl;
}

void Singer::Get()
{
    cout << "Enter number for singer's vocal range:\n";
    int i;
    for (i = 0; i < Vtypes; i++)
    {
        cout << i << ": " << pv[i] << "   ";
        if (i % 4 == 3)
            cout << endl;
    }
    if (i % 4 != 0)
        cout << '\n';
    cin >> voice;
    while (cin.get() != '\n')
        continue;
}

// SingingWaiter methods
void SingingWaiter::Data() const
{
    Singer::Data();
    Waiter::Data();
}

void SingingWaiter::Get()
{
    Waiter::Get();
    Singer::Get();
}

void SingingWaiter::Set()
{
    cout << "Enter singing waiter's name: ";
    Worker::Get();
    Get();
}

void SingingWaiter::Show() const
{
    cout << "Category: singing waiter\n";
    Worker::Data();
    Data();
}
// workmi.cpp -- multiple inheritance
// compile with workermi.cpp
#include <iostream>
#include <cstring>
#include "workermi.h"
const int SIZE = 5;

int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    using std::strchr;

    Worker * lolas[SIZE];

    int ct;
    for (ct = 0; ct < SIZE; ct++)
    {
        char choice;
        cout << "Enter the employee category:\n"
            << "w: waiter  s: singer  "
            << "t: singing waiter  q: quit\n";
        cin >> choice;
        while (strchr("wstq", choice) == NULL)
        {
            cout << "Please enter a w, s, t, or q: ";
            cin >> choice;
        }
        if (choice == 'q')
            break;
        switch(choice)
        {
            case 'w':   lolas[ct] = new Waiter;
                        break;
            case 's':   lolas[ct] = new Singer;
                        break;
            case 't':   lolas[ct] = new SingingWaiter;
                        break;
        }
        cin.get();
        lolas[ct]->Set();
    }

    cout << "\nHere is your staff:\n";
    int i;
    for (i = 0; i < ct; i++)
    {
        cout << endl;
        lolas[i]->Show();
    }
    for (i = 0; i < ct; i++)
        delete lolas[i];
    cout << "Bye.\n";
    return 0;
}


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。