从 《Accelerated C++》源码学习句柄类

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从 《Accelerated C++》源码学习句柄类相关的知识,希望对你有一定的参考价值。

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个计算结果应该一致。

 技术分享

 

以上是关于从 《Accelerated C++》源码学习句柄类的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV中的快速特征检测——FAST(Features from Accelerated Segment Test)

句柄C++

MFC C++ 从 WM_KEYDOWN 处理程序获取在 OnDraw() 中编辑的 CDC pDC 的句柄

Accelerated Mobile Page 和表格突出显示在屏幕上

求c++编程的案例,从入门到复杂的

OPcache max_accelerated_files 是如何工作的?