code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类

时间:2023-03-09 00:23:07
code of C/C++(3) - 从 《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(), final() { }
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() { }
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 = ; // 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 = ; i != students.size(); ++i) {
cout << students[i].name()
<< string(maxlen + - students[i].name().size(), ' ');
try {
double final_grade = students[i].grade(); // `Core::grade'
streamsize prec = cout.precision();
cout << setprecision() << 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 = ; 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 = ; i != students.size(); ++i) {
cout << students[i]->name()
<< string(maxlen + - students[i]->name().size(), ' ');
try {
double final_grade = students[i]->grade();
streamsize prec = cout.precision();
cout << setprecision() << final_grade
<< setprecision(prec) << endl; } catch (domain_error e) {
cout << e.what() << endl;
}
delete students[i];
}
return ;
}

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() { }
Student_info(std::istream& is): cp() { 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()
{
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 = ;
}
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 = ;

         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 = ;

              i != students.size(); ++i) {

                   cout << students[i].name()

                        << string(maxlen +  - students[i].name().size(), ' ');

                   try {

                            double final_grade = students[i].grade();

                            streamsize prec = cout.precision();

                            cout << setprecision() << final_grade

                                 << setprecision(prec) << endl;

                   } catch (domain_error e) {

                            cout << e.what() << endl;

                   }

         }

         return ;
}

自己实现句柄类机制

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() { }
A(int a) { len = a;}
A(istream& is) {read(is);}; virtual istream& read(istream& is); virtual int sum() {return len + ;}
virtual ~A(){}; }; istream& A::read(istream& is) {
is >> len;
return is;
} class B : public A {
public:
B():HandleB () { }
B(int b) { HandleB = b; }
B(istream& is) {read(is);} istream& read(istream& is); int sum() { return HandleB * ;}
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(){}
istream& read(istream& is); Handle(Handle& f) { if(f.pa) pa = f.pa->clone(); else pa = ;}
Handle& operator= (Handle& f) {
if( &f != this){
delete pa;
if(f.pa) pa = f.pa->clone();
else pa = ;
}
return *this;
}
int sum () {
if(pa) return pa->sum();
else {
pa = ;
return ;}
}
~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 = ;
}
return is;
} void main ()
{
Handle f;
A a(); 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个计算结果应该一致。

code of C/C++(3) - 从 《Accelerated C++》源码学习句柄类