Lab 11-1: Inheritance

   
實習目標 練習 C++ 中繼承的語法
   
步驟一 這個作業中我們嘗試擴充上一次實習的 StudentList 與 Iterator 這一組容器類別, 我們希望能夠在不更動 StudentList 與 Iterator 類別的程式情況下 (這是所謂的 OCP, Open-Closed Principle, 1, 2, 3), 製作一個 LoggedStudentList, 這個類別基本上具有完整的 StudentList 類別所提供的功能, 但是在建構元中需要指定一個已經開啟的文字檔案串流以供記錄所有操作過程, 同時類別中多了一個 dump() 函式可以將所有串列中的學生資料列印在檔案串流中, 在執行 void appendEntry(Student *student), void insertEntry(Iterator iter, Student *student), 以及 bool deleteEntry(char *id) 時也都會在記錄檔案中列印一列訊息顯示目前的動作, 檔案中列印的結果如下:
LoggedStudentList::appendEntry()
    [Mary Chen, 111111111, 0933111111, Business]
LoggedStudentList::appendEntry()
    [John Wang, 222222222, 0928222222, Computer Science]
LoggedStudentList::appendEntry()
    [Mel Lee, 333333333, 0968333333, Mechanical Engineering]
LoggedStudentList::appendEntry()
    [Bob Tsai, 444444444, 0930444444, Electrical Engineering]
LoggedStudentList::appendEntry()
    [Ron Yang, 555555555, 0918555555, Computer Science]
LoggedStudentList::deleteEntry() id=444444444
LoggedStudentList::insertEntry()
    [Carol Chen, 333331111, 0933333111, Business]
List dumping
    Element[0]: [Mary Chen, 111111111, 0933111111, Business]
    Element[1]: [John Wang, 222222222, 0928222222, Computer Science]
    Element[2]: [Mel Lee, 333333333, 0968333333, Mechanical Engineering]
    Element[3]: [Carol Chen, 333331111, 0933333111, Business]
    Element[4]: [Ron Yang, 555555555, 0918555555, Computer Science]
步驟二 請下載上一次實習的程式, 其中的四個類別的定義如下:
    class Student
    {
    public:
        Student();
        Student(const char *name, const char *ID, 
                const char *phone, const char *department);
        virtual ~Student();
        bool IDEquals(const char *id) const;
        void display(ostream &os) const;
        bool ofTheSameDepartment(Student &student2) const;
    private:
        char *m_name;
        char *m_ID;
        char *m_phone;
        char *m_department;
    };
    
    class StudentList
    {
        friend class StudentListIterator;
        class Node
        {
            friend StudentList;
            friend class StudentListIterator;
        public:
            Node(Student *data);
            virtual ~Node();
        private:
            Student *m_data;
            Node *m_next;
        };
    public:
        StudentList();
        virtual ~StudentList();
        void appendEntry(Student *student);
        void insertEntry(StudentListIterator iter, Student *student); 
        Student *find(char *id);
        bool deleteEntry(char *id);
        Student *&operator[](int slot);
        int size() const;
    private:
        Node *m_head, *m_tail;
        int  m_size;
        static Node m_dummy;
    };
    
    class StudentListIterator
    {
        friend class StudentList;
    public:
        StudentListIterator(StudentList &list);
        void reset();
        void next();
        Student &operator*() const;
        Student *operator->() const;
        bool hasMoreData() const;
    private:
        StudentList::Node *m_iterator;
        StudentList *m_list;
        static Student m_dummy;
    };
請不要修改這四個類別的任何定義 (上次實習在步驟八中已經將 Iterator 類別改名為 StudentListIterator 了)
步驟三 請修改你的主程式如下 (紅色部份為使用 LoggedStudentList, 其它部份和原來的程式是相同的):
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    #include "Student.h"
    #include "LoggedStudentList.h"
    #include "StudentListIterator.h"
    
    void main()
    {
        ofstream logfile("main2.log");
        LoggedStudentList sList(logfile);
        sList.appendEntry(new Student("Mary Chen", "111111111", 
                                      "0933111111", 
                                      "Business"));
        sList.appendEntry(new Student("John Wang", "222222222", 
                                      "0928222222", 
                                      "Computer Science"));
        sList.appendEntry(new Student("Mel Lee", "333333333", 
                                      "0968333333", 
                                      "Mechanical Engineering"));
        sList.appendEntry(new Student("Bob Tsai", "444444444", 
                                      "0930444444", 
                                      "Electrical Engineering"));
        sList.appendEntry(new Student("Ron Yang", "555555555", 
                                      "0918555555", 
                                      "Computer Science"));
    
        int i;
        StudentListIterator iter1(sList), iter2(sList);
        for (i=0, iter1.reset(); 
             iter1.hasMoreData(); iter1.next(), i++)
        {
            cout << i << ":";
            iter1->display(cout);
            cout << endl;
        }
    
        sList.find("222222222")->display(cout); cout << endl;
    
        if (sList.deleteEntry("444444444"))
            cout << "Bob Tsai's entry deleted successfully!\n";
        else
            cout << "Bob Tsai's entry deletion failed!\n";
    
        if (sList.find("444444444") == 0)
            cout << "Can not find Bob Tsai's entry!\n";
    
    
        cout << endl;
        for (iter1.reset(); iter1.hasMoreData(); iter1.next())
        {
            for (iter2=iter1, iter2.next(); 
                 iter2.hasMoreData(); iter2.next())
            {
                if (iter1->ofTheSameDepartment(*iter2))
                {
                    cout << "The following two students"
                            " are of the same department:\n";
                    iter1->display(cout);
                    cout << endl;
                    iter2->display(cout);
                    cout << endl;
                }
            }
        }
    
        cout << endl;
        for (iter1.reset(); iter1.hasMoreData(); iter1.next())
            if (iter1->IDEquals("333333333"))
                sList.insertEntry(iter1, 
                    new Student("Carol Chen", "333331111", 
                                "0933333111", "Business"));
        for (i=0; i<sList.size(); i++)
        {
            sList[i]->display(cout);
            cout << endl;
        }
    
        sList.dump();
    }
步驟四 請定義新的類別 LoggedStudentList, 這個類別需要繼承 StudentList 類別, 你可以手動編輯或是在使用 Visual Studio 界面 new Class 時選擇新類別的基礎類別為 StudentList 類別, 如果你是手動編輯的話, 請記得在 class LoggedStudentList : public StudentList 敘述之前要 include "StudentList.h", 基本程式碼如下:
    #include "StudentList.h"

    class LoggedStudentList : public StudentList
    {
    public:
    private:
    };
步驟五 如步驟一的說明, 由於我們希望在所有操作list的動作時都在指定的檔案中記錄下來, 所以在這個類別中我們要定義五個成員函式 (包括 ctor, appendEntry, insertEntry, deleteEntry, dump), 其中 appendEntry, insertEntry, 和 deleteEntry 這些成員函式的名稱和 父類別 StudentList 中的定義需要完全一樣, 如此在步驟三中所描述的 main() 的內容才能夠順利執行
    #include "StudentList.h"

    class LoggedStudentList : public StudentList
    {
    public:
        LoggedStudentList(ofstream &ofs);
        void appendEntry(Student *student);
        void insertEntry(StudentListIterator iter, Student *student); 
        bool deleteEntry(char *id);
        void dump();
    private:
    };
步驟六 這個類別需要定義一個參考資料成員, 記錄由建構元所傳入的檔案串流的參考
    class LoggedStudentList : public StudentList
    {
    public:
        ...
    private:
        ofstream &m_ofs;
    };
接下來請撰寫相關的五個成員函式

  1. 建構元 LoggedStudentList(ofstream &ofs): 需要初始化 m_ofs 成員, 請注意只有唯一的一種初始化方法:初始化串列 (initialization list)

  2. void appendEntry(Student *student);
    void insertEntry(StudentListIterator iter, Student *student);
    bool deleteEntry(char *id);

    這三個函式最主要需要在串流檔案中記錄下相關資料, 然後呼叫父類別中的同名函式來完成其功能, 例如:

        m_ofs << "LoggedStudentList::appendEntry()\n    ";
        student->display(m_ofs); 
        m_ofs << endl;
        StudentList::appendEntry(student);

  3. void dump() 函式
步驟七 測試一下吧! 欣賞一下你在 LoggedStudentList 中所新增的程式碼, 應該只有 50 列左右吧! 可是這個串列類別除了有和上一次實習所製作的 StudentList 一模一樣的功能之外, 還有新增加的功能, 同時 StudentListIterator 類別也可以正常工作, 這就是繼承的語法所帶來的第一種好處 程式碼的重用 (code reuse),

我們常常會繼承一些已經設計好 (完整測試過) 的類別, 新增或是修改一點點功能來使得這個類別適合目前的程式的需求。

步驟八 請助教檢查後, 將所完成的 專案 (只需保留 .cpp, .h, .sln 以及 .vcxproj 檔案即可; 刪除掉 .suo, .sdf, .filters, .users, debug\ 資料匣, 以及 ipch\ 資料匣下的所有內容) 壓縮起來, 選擇 Lab11-1 上傳, 後面的實習課程可能需要使用這裡所完成的程式

C++ 物件導向程式設計課程 首頁

製作日期: 04/05/2004 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程學系 Lagoon