• code of C/C++(3)


    0  C++中多态的概念

    多态是指通过基类的指针或者引用,利用虚函数机制,在运行时确定对象的类型,并且确定程序的编程策略,这是OOP思想的核心之一。多态使得一个对象具有多个对象的属性。class Core作为就算成绩的基类、class Grad为Core的子类,添加了论文(thesis)成绩,Grad最终成绩为论文成绩和基类计算方法得到的成绩中的较小值。这是一个知识点:继承的适用场合就是,子类和基类的功能是相同或者相似,但是子类多了一些扩展。

    //filename:Core.h
    #ifndef GUARD_Core_h
    #define GUARD_Core_h
    
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <vector>
    
    class Core {
    public:
        Core(): midterm(0), final(0) { }
    Core(std::istream& is) { read(is); }
    
        std::string name() const;
    
        virtual std::istream& read(std::istream&);
    virtual double grade() const;
    
    virtual ~Core() { }
    protected:
        std::istream& read_common(std::istream&);
    double midterm, final;
        std::vector<double> homework;
    
    virtual Core* clone() const { return new Core(*this); }
    private:
        std::string n;
        friend class Student_info;
    };
    
    class Grad: public Core {
    public:
    Grad(): thesis(0) { }
    Grad(std::istream& is) { read(is); }
    
    double grade() const;
    std::istream& read(std::istream&);
    private:
    double thesis;                                                                      Grad* clone() const { return new Grad(*this); }
    };

    1  使用句柄类的必要性推导及句柄类实现及示例

    接下来讨论如何使用这两个类,因为这两个类是不同的类型,各自的read,grade函数都有不同的定义,如何简明地使用两个类就成了一个要解决的问题。

    方案1是对于每个学生,先判断下是什么类型,然后声明这种类型的对象,调用类中定义的grade函数完成成绩统计工作,使用Core的方法如下,Grad的使用将以下程序的Core替换为Grad。

    //main_1.cpp use Core& Grad
    vector<Core> students;          
        Core record;
        string::size_type maxlen = 0;
    
        // read and store the data
        while (record.read(cin)) {
            maxlen = max(maxlen, record.name().size());
            students.push_back(record);
        }
    
        // alphabetize the student records
        sort(students.begin(), students.end(), compare);
    
        // write the names and grades
        for (vector<Core>::size_type i = 0; i != students.size(); ++i) {
            cout << students[i].name()
                 << string(maxlen + 1 - students[i].name().size(), ' ');
            try {
                double final_grade = students[i].grade(); // `Core::grade'
                streamsize prec = cout.precision();
                cout << setprecision(3) << final_grade
                     << setprecision(prec) << endl;
            } catch (domain_error e) {
                cout << e.what() << endl;
            }
        }

    方案2:对于方案1,为了使用Core、Grad的使用,代码极大的重复,不是好的编程实践。方案2的改进是声明一个Core*类型的对象,通过Core*类型调用虚函数,以多态的方式完成程序。

    //main_2.cpp
    int main()
    {
        vector<Core*> students;        
        Core* record;                   
        char ch;
        string::size_type maxlen = 0;
    
        while (cin >> ch) {
            if (ch == 'U')
                record = new Core;              
    else
                record = new Grad;      
            record->read(cin);          
            maxlen = max(maxlen, record->name().size());
            students.push_back(record);
        }
    sort(students.begin(), students.end(), compare_Core_ptrs);
    
        for (std::vector<Core*>::size_type i = 0; i != students.size(); ++i) {
            cout << students[i]->name()
                 << string(maxlen + 1 - students[i]->name().size(), ' ');
            try {
                double final_grade = students[i]->grade();
                streamsize prec = cout.precision();
                cout << setprecision(3) << final_grade
                     << setprecision(prec) << endl;
    
            } catch (domain_error e) {
                cout << e.what() << endl;
            }
            delete students[i];        
        }
        return 0;
    }

    3  句柄类接口与实现

    方案2精简了代码,并且保证了调用不同方案的灵活性,但这样的方案有个缺点:编程中需要随时记得为不同的对象分配空间,在对象使用后要记得销毁Core*分配的对象,回收空间,增加了程序出错的可能性。句柄类的优势就是将类似工作交给专门的句柄类完成,程序员使用句柄类完成统计成绩的工作。

    //student_info.h
    #ifndef GUARD_Student_info_h
    #define GUARD_Student_info_h
    
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <vector>
    
    #include "Core.h"
    
    class Student_info {
    public:
        // constructors and copy control
        Student_info(): cp(0) { }
        Student_info(std::istream& is): cp(0) { read(is); }
        Student_info(const Student_info&);
        Student_info& operator=(const Student_info&);
        ~Student_info() { delete cp; }
    
        // operations
        std::istream& read(std::istream&);
    
        std::string name() const {
            if (cp) return cp->name();
            else throw std::runtime_error("uninitialized Student");
        }
        double grade() const {
            if (cp) return cp->grade();
            else throw std::runtime_error("uninitialized Student");
        }
    
        static bool compare(const Student_info& s1,
                            const Student_info& s2) {
            return s1.name() < s2.name();
        }
    
    private:
        Core* cp;
    };
    
    #endif
    //student_info.cpp
    
    #include <iostream>
    
    #include "Core.h"
    #include "Student_info.h"
    
    using std::istream;
    
    istream& Student_info::read(istream& is)
    {
        delete cp;          
        char ch;
        is >> ch;           
        if (ch == 'U') {
            cp = new Core(is);
        } else {
            cp = new Grad(is);
        }
    
        return is;
    }
    
    Student_info::Student_info(const Student_info& s): cp(0)
    {
        if (s.cp) cp = s.cp->clone();
    }
    
    Student_info& Student_info::operator=(const Student_info& s)
    {
        if (&s != this) {
            delete cp;
            if (s.cp)
                cp = s.cp->clone();
            else
                cp = 0;
        }
        return *this;
    }

    代码说明:student_info类中封装了基类指针,调用Core或者Grad中定义的方法,所以Student_info中要有和Core类相同的接口,该类的拷贝构造函数要得到指针指向的类的信息,得到原来值得副本,这个副本由clone函数得到,所以在Core和Grad中定义不同的clone函数获得当前值得副本:

    virtual Core* clone() const { return new Core (*this); }

    virtual Grad* clone() const { return new Grad (*this); }

    因为Core和Grad都有各自的拷贝构造函数,所以将clone函数声明放在protected中,virtual是可以继承的,一般而言子类中继承virtual函数的参数和返回类型应该与基类相同,但是通过基类指针和基类引用调用的虚函数重新定义的时候,返回类型可以是子类指针或者子类引用类型,Grad* clone( )为protected或者private标签下都可以。

    使用句柄类

    //main_3.cpp
    
    #include "Student_info.h"
    
     
    
    using std::cin;
    
    using std::cout;
    
    using std::domain_error;
    
    using std::endl;
    
    using std::setprecision;
    
    using std::setw;
    
    using std::sort;
    
    using std::streamsize;
    
    using std::string;
    
    using std::vector;
    
     
    
    #ifdef _MSC_VER
    
    #include "../minmax.h"
    
    #else
    
    using std::max;
    
    #endif
    
     
    
    int main()
    
    {
    
             vector<Student_info> students;
    
             Student_info record;
    
             string::size_type maxlen = 0;
    
     
    
             while (record.read(cin)) {
    
                       maxlen = max(maxlen, record.name().size());
    
                       students.push_back(record);
    
             }
    
              sort(students.begin(), students.end(), Student_info::compare);
    
              for (std::vector<Student_info>::size_type i = 0;
    
                  i != students.size(); ++i) {
    
                       cout << students[i].name()
    
                            << string(maxlen + 1 - students[i].name().size(), ' ');
    
                       try {
    
                                double final_grade = students[i].grade();
    
                                streamsize prec = cout.precision();
    
                                cout << setprecision(3) << final_grade
    
                                     << setprecision(prec) << endl;
    
                       } catch (domain_error e) {
    
                                cout << e.what() << endl;
    
                       }
    
             }
    
             return 0;
       }

    自己实现句柄类机制

    5  实现代码

    A——基类,B——A的子类,Handle——句柄类,检测拷贝构造函数、operator=是否正确,代码如下:

    #include <iostream>
    using namespace std;
    
    class A {
    protected:
        int len;
        virtual A* clone()  {return new A(*this);}
    private:
        int HandleA;
        friend class Handle;
    public:
        A():len(0) { }
        A(int a) { len = a;}
        A(istream& is) {read(is);};
        
        virtual istream& read(istream& is);
    
        virtual int sum() {return len + 100;} 
        virtual ~A(){};
    
    };
    
    istream& A::read(istream& is) {
        is >> len;
        return is;
    }
    
    
    
    class B : public A {
    public:
        B():HandleB (9) { }
        B(int b)     { HandleB = b; }
        B(istream& is) {read(is);}
    
        istream& read(istream& is);
    
        int sum()    { return HandleB * 123;}
    protected:     
        B* clone()   {return new B(*this);}    //private & protected both OK!
    private:
        
        int HandleB;
    };
    
    istream& B::read(istream& is) {
        is >> HandleB;
        return is;
    }
    
    class Handle {
    public:
        Handle():pa(0){}
        istream& read(istream& is);
    
        Handle(Handle& f) { if(f.pa) pa = f.pa->clone(); else pa = 0;}  
        Handle& operator= (Handle& f) {
            if( &f != this){
                delete pa;
                if(f.pa) pa = f.pa->clone();
                else pa = 0;
            }
            return *this;
        }
        int sum () {
            if(pa) return pa->sum();
            else { 
            pa = 0;
            return 233 ;}
        }
        ~Handle() {delete pa ;}
    
    private:
        A* pa;
    };
    istream& Handle::read(istream& is) {    
        delete pa;
        char ch;
        is >> ch;
        if (ch =='a' || ch == 'A'){
            pa = new A (is);
        } 
        else if(ch =='b' || ch == 'B') {
            pa = new B(is);
        }
        else {
            pa = 0;
        }
        return is;
    }
    
    void main ()
    {
        Handle f;
        A a(12);
    
        f.read(cin);
        Handle g(f);   
        Handle h = f;
    
        cout << f.sum() << endl;
        cout << g.sum() << endl;
        cout << h.sum() << endl;
    }

    // 第一个计算结果由句柄对象f调用sum函数得到,第二个计算结果拷贝f初始化g,调用g的sum得到,第三个计算机结果先用f赋值给h,调用h的sum计算得到,3个计算结果应该一致。

     

  • 相关阅读:
    THINKphp学习笔记
    Js获取当前日期时间及其它操作
    Oracle数据导入导出imp/eXP
    SQL中的单记录函数
    Windows服务C#/VS2003
    oracle数据库开发的一些经验积累
    Oracle 数据库的安全策略
    高兴
    无法正确运行的C#程序
    最详细的Visual C++ 2008 Express Edition使用方法(图文)
  • 原文地址:https://www.cnblogs.com/hanxinle/p/5228218.html
Copyright © 2020-2023  润新知